diff --git a/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_01.robot b/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_01.robot new file mode 100644 index 0000000000000000000000000000000000000000..d54c4dad145c5f996c0d14f1ff18cd15d360ac80 --- /dev/null +++ b/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_01.robot @@ -0,0 +1,64 @@ +*** Settings *** +Documentation Check that one can replace an existing entity and that its createdAt Temporal Property remains unchanged + +Resource ${EXECDIR}/resources/ApiUtils/ContextInformationProvision.resource +Resource ${EXECDIR}/resources/ApiUtils/ContextInformationConsumption.resource +Resource ${EXECDIR}/resources/AssertionUtils.resource +Resource ${EXECDIR}/resources/JsonUtils.resource + +Test Setup Setup Initial Entity +Test Teardown Delete Initial Entity + + +*** Variables *** +${building_id_prefix} urn:ngsi-ld:Building: +${entity_filename} building-simple-attributes.json +${fragment_filename} building-locatedAt-and-name.json +${expectation_filename} building-locatedAt-and-name-normalized.jsonld + + +*** Test Cases *** +054_01_01 Replace an existing entity + [Documentation] Check that one can replace an existing entity and that its createdAt Temporal Property remains unchanged + [Tags] e-replace 5_6_18 6_5_3_3 since_v1.6.1 + ${entity}= Load Entity ${fragment_filename} ${entity_id} + ${response}= Replace Entity Selecting Content Type + ... entity_id=${entity_id} + ... entity_fragment=${entity} + ... content_type=${CONTENT_TYPE_LD_JSON} + Check Response Status Code 204 ${response.status_code} + ${response1}= Retrieve Entity by Id + ... id=${entity_id} + ... context=${ngsild_test_suite_context} + ... options=sysAttrs + ${ignored_attributes}= Create List @context createdAt modifiedAt + ${entity_expectation_payload}= Load Test Sample entities/expectations/${expectation_filename} ${entity_id} + Check Updated Resource Set To + ... updated_resource=${entity_expectation_payload} + ... response_body=${response1.json()} + ... ignored_keys=${ignored_attributes} + Check Response Body Containing an Attribute set to + ... expected_attribute_name=createdAt + ... response_body=${response1.json()} + ... expected_attribute_value=${createdAt} + + +*** Keywords *** +Setup Initial Entity + ${entity_id}= Generate Random Entity Id ${building_id_prefix} + Set Test Variable ${entity_id} + ${response}= Create Entity Selecting Content Type + ... ${entity_filename} + ... ${entity_id} + ... ${CONTENT_TYPE_JSON} + ... ${ngsild_test_suite_context} + Check Response Status Code 201 ${response.status_code} + ${response}= Retrieve Entity by Id + ... id=${entity_id} + ... context=${ngsild_test_suite_context} + ... options=sysAttrs + ${createdAt}= Set Variable ${response.json()['createdAt']} + Set Test Variable ${createdAt} + +Delete Initial Entity + Delete Entity by Id ${entity_id} diff --git a/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_02.robot b/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_02.robot new file mode 100644 index 0000000000000000000000000000000000000000..85c7acd4092b1b8b02b72b9aaec84a20cab00223 --- /dev/null +++ b/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_02.robot @@ -0,0 +1,53 @@ +*** Settings *** +Documentation Check that if the target entity ID is not a valid URI or it's not present a 400 error is raised + +Resource ${EXECDIR}/resources/ApiUtils/ContextInformationProvision.resource +Resource ${EXECDIR}/resources/ApiUtils/ContextInformationConsumption.resource +Resource ${EXECDIR}/resources/AssertionUtils.resource +Resource ${EXECDIR}/resources/JsonUtils.resource + +Test Setup Setup Initial Entity +Test Teardown Delete Initial Entity +Test Template Replace an existing entity with invalid/not present ID + + +*** Variables *** +${building_id_prefix} urn:ngsi-ld:Building: +${entity_filename} building-simple-attributes.json +${fragment_filename} building-locatedAt-and-name.json +${invalid_id} invalidUri + + +*** Test Cases *** FAULTY_ENTITY_ID +054_02_01 Replace an existing entity giving an invalid Id + [Tags] e-replace 5_6_18 6_5_3_3 since_v1.6.1 + ${invalid_id} +054_02_02 Replace an existing entity without giving an Id + [Tags] e-replace 5_6_18 6_5_3_3 since_v1.6.1 + ${EMPTY} + + +*** Keywords *** +Replace an existing entity with invalid/not present ID + [Documentation] Check that if the target entity ID is not a valid URI or it's not present a 400 error is raised + [Tags] e-replace 5_6_18 6_5_3_3 since_v1.6.1 + [Arguments] ${faulty_entity_id} + ${entity}= Load Entity ${fragment_filename} ${entity_id} + ${response}= Replace Entity Selecting Content Type + ... entity_id=${faulty_entity_id} + ... entity_fragment=${entity} + ... content_type=${CONTENT_TYPE_LD_JSON} + Check Response Status Code 400 ${response.status_code} + +Setup Initial Entity + ${entity_id}= Generate Random Entity Id ${building_id_prefix} + Set Test Variable ${entity_id} + ${response}= Create Entity Selecting Content Type + ... ${entity_filename} + ... ${entity_id} + ... ${CONTENT_TYPE_JSON} + ... ${ngsild_test_suite_context} + Check Response Status Code 201 ${response.status_code} + +Delete Initial Entity + Delete Entity by Id ${entity_id} diff --git a/data/entities/building-locatedAt-and-name.json b/data/entities/building-locatedAt-and-name.json new file mode 100644 index 0000000000000000000000000000000000000000..9cac6ee659d65b242b71b9014626962543c8fd45 --- /dev/null +++ b/data/entities/building-locatedAt-and-name.json @@ -0,0 +1,15 @@ +{ + "id": "urn:ngsi-ld:Building:randomUUID", + "type": "Building", + "name": { + "type": "Property", + "value": "Pisa Tower" + }, + "locatedAt": { + "type": "Relationship", + "object": "urn:ngsi-ld:City:Pisa" + }, + "@context": [ + "https://forge.etsi.org/rep/cim/ngsi-ld-test-suite/-/raw/develop/resources/jsonld-contexts/ngsi-ld-test-suite-compound.jsonld" + ] +} \ No newline at end of file diff --git a/data/entities/expectations/building-locatedAt-and-name-normalized.jsonld b/data/entities/expectations/building-locatedAt-and-name-normalized.jsonld new file mode 100644 index 0000000000000000000000000000000000000000..9cac6ee659d65b242b71b9014626962543c8fd45 --- /dev/null +++ b/data/entities/expectations/building-locatedAt-and-name-normalized.jsonld @@ -0,0 +1,15 @@ +{ + "id": "urn:ngsi-ld:Building:randomUUID", + "type": "Building", + "name": { + "type": "Property", + "value": "Pisa Tower" + }, + "locatedAt": { + "type": "Relationship", + "object": "urn:ngsi-ld:City:Pisa" + }, + "@context": [ + "https://forge.etsi.org/rep/cim/ngsi-ld-test-suite/-/raw/develop/resources/jsonld-contexts/ngsi-ld-test-suite-compound.jsonld" + ] +} \ No newline at end of file diff --git a/doc/analysis/generaterobotdata.py b/doc/analysis/generaterobotdata.py index 9b2b7a918134f220671843d57823e097ff44c432..5f4c7c0673116818c223a66f7b0e5d28430a125a 100644 --- a/doc/analysis/generaterobotdata.py +++ b/doc/analysis/generaterobotdata.py @@ -43,6 +43,7 @@ class GenerateRobotData: 'Discovery/RetrieveDetailsOfAvailableAttributes': 'DISC', 'Entity/RetrieveEntity': 'E', 'Entities/CreateEntity': 'E', + 'Entities/ReplaceEntity': 'E', 'Entity/QueryEntities': 'E', 'Entities/DeleteEntity': 'E', 'EntityAttributes/AppendEntityAttributes': 'EA', diff --git a/doc/analysis/requests.py b/doc/analysis/requests.py index 3899e01e9b87867bff9effe4d78ee36d16b9e18b..f975d5226d258b2c0449a30d3f8ecb19fe382a73 100644 --- a/doc/analysis/requests.py +++ b/doc/analysis/requests.py @@ -78,7 +78,7 @@ class Requests: }, 'Retrieve Entity by Id': { 'positions': [], - 'params': ['id', 'accept', 'context'] + 'params': ['id', 'accept', 'context', 'options'] }, 'Query Entities': { 'positions': [], @@ -102,6 +102,10 @@ class Requests: 'positions': [0], 'params': ['id'] }, + 'Replace Entity Selecting Content Type': { + 'positions': [0, 1, 2, 3], + 'params': ['entity_id', 'entity_fragment', 'content_type', 'context'] + }, 'Append Entity Attributes': { 'positions': [0, 1, 2], 'params': ['id', 'fragment_filename', 'content_type'] @@ -306,6 +310,8 @@ class Requests: Requests.query_entities, 'Delete Entity by Id': Requests.delete_entity_by_id, + 'Replace Entity Selecting Content Type': + Requests.replace_entity_selecting_content_type, 'Append Entity Attributes': Requests.append_entity_attributes, 'Update Entity Attributes': @@ -1302,10 +1308,9 @@ class Requests: return response - @staticmethod def retrieve_entity_by_id(kwargs) -> str: - expected_parameters = ['id', 'accept', 'context'] + expected_parameters = ['id', 'accept', 'context', 'options'] result = [x for x in kwargs if x not in expected_parameters] response = 'Request Retrieve Entity by Id' @@ -1317,6 +1322,8 @@ class Requests: response = f"{response} and\n Query Parameter: accept set to '{value}'" case 'context': response = f"{response} and\n Query Parameter: context set to '{value}'" + case 'options': + response = f"{response} and\n Query Parameter: options set to '{value}'" # If an exact match is not confirmed, this last case will be used if provided case _: raise Exception(f"ERROR, unexpected attribute '{result}', the attributes expected are " @@ -1329,6 +1336,31 @@ class Requests: if 'id' in kwargs: return f"Delete Entity Request with id set to '{kwargs['id']}'" + @staticmethod + def replace_entity_selecting_content_type(kwargs) -> str: + expected_parameters = ['entity_id', 'entity_fragment', 'content_type', 'context'] + + if 'context' not in kwargs: + kwargs['context'] = '${EMPTY}' + + result = [x for x in kwargs if x not in expected_parameters] + response = "Replace Entity Selecting Content Type" + for key, value in kwargs.items(): + match key: + case 'entity_id': + response = f"{response} and\n Query Parameter: entity_id set to '{value}'" + case 'entity_fragment': + response = f"{response} and\n Query Parameter: entity_fragment set to '{value}'" + case 'content_type': + response = f"{response} and\n Query Parameter: content_type set to '{value}'" + case 'context': + response = f"{response} and\n Query Parameter: context set to '{value}'" + case _: + raise Exception(f"ERROR, unexpected attribute '{result}', the attributes expected are " + f"'{expected_parameters}', but received: {kwargs}") + + return response + @staticmethod def delete_subscription(kwargs) -> str: if 'id' in kwargs: @@ -1636,4 +1668,3 @@ class Requests: return value except KeyError: return data - diff --git a/doc/files/ContextInformation/Provision/054_01.json b/doc/files/ContextInformation/Provision/054_01.json new file mode 100644 index 0000000000000000000000000000000000000000..023c5d1e5cfacdd41a0989afb8f84215966b76e9 --- /dev/null +++ b/doc/files/ContextInformation/Provision/054_01.json @@ -0,0 +1,41 @@ +{ + "tp_id": "TP/NGSI-LD/CI/Prov/E/054_01", + "test_objective": "Check that one can replace an existing entity and that its createdAt Temporal Property remains unchanged", + "reference": "ETSI GS CIM 009 V1.6.1 [], clauses 5.6.18, 6.5.3.3", + "config_id": "", + "parent_release": "v1.6.1", + "clauses": [ + "5.6.18", + "6.5.3.3" + ], + "pics_selection": "", + "keywords": [ + "Setup Initial Entity", + "Delete Initial Entity" + ], + "teardown": "None", + "initial_condition": "with {\n the SUT being in the \"initial state\" and\n the SUT containing an initial Entity ${entity} \n with an id set to ${entityId} \n}", + "test_cases": [ + { + "name": "054_01_01 Replace an existing entity", + "permutation_tp_id": "TP/NGSI-LD/CI/Prov/E/054_01_01", + "doc": "Check that one can replace an existing entity and that its createdAt Temporal Property remains unchanged", + "tags": [ + "5_6_18", + "6_5_3_3", + "e-replace", + "since_v1.6.1" + ], + "setup": "Setup Initial Entity", + "teardown": "Delete Initial Entity", + "template": null, + "then": "then {\n the SUT sends a valid Response for the operations:\n Replace Entity Selecting Content Type with Response Status Code set to 204 and\n Retrieve Entity by Id with Check Updated Entity and\n Query Parameter: 'updated_resource' set to 'entity_expectation_payload' and\n Query Parameter: 'response_body' set to 'response1.json()' and\n Query Parameter: 'ignored_keys' set to 'ignored_attributes' and\n Retrieve Entity by Id with Check Response Body containing an Attribute set to and\n Query Parameter: expected_attribute_name set to 'createdAt' and\n Query Parameter: response_body set to 'response1.json()' and\n Query Parameter: expected_attribute_value set to 'createdAt'\n}", + "when": "when {\n the SUT receives a Request from the client containing:\n URL set to '/ngsi-ld/v1/entities/{entity_id}'\n method set to 'PUT'\n Replace Entity Selecting Content Type and\n Query Parameter: entity_id set to '${entity_id}' and\n Query Parameter: entity_fragment set to '${entity}' and\n Query Parameter: content_type set to 'application/ld+json' and\n Query Parameter: context set to ''\n}", + "http_verb": "PUT", + "endpoint": "entities/{entity_id}" + } + ], + "permutations": [], + "robotpath": "ContextInformation/Provision/Entities/ReplaceEntity", + "robotfile": "054_01" +} \ No newline at end of file diff --git a/doc/files/ContextInformation/Provision/054_02.json b/doc/files/ContextInformation/Provision/054_02.json new file mode 100644 index 0000000000000000000000000000000000000000..84a4982cafb12c7c523f834736b8b467e61070a2 --- /dev/null +++ b/doc/files/ContextInformation/Provision/054_02.json @@ -0,0 +1,62 @@ +{ + "tp_id": "TP/NGSI-LD/CI/Prov/E/054_02", + "test_objective": "Check that if the target entity ID is not a valid URI or it's not present a 400 error is raised", + "reference": "ETSI GS CIM 009 V1.6.1 [], clauses 5.6.18, 6.5.3.3", + "config_id": "", + "parent_release": "v1.6.1", + "clauses": [ + "5.6.18", + "6.5.3.3" + ], + "pics_selection": "", + "keywords": [ + "Replace an existing entity with invalid/not present ID", + "Setup Initial Entity", + "Delete Initial Entity" + ], + "teardown": "None", + "initial_condition": "with {\n the SUT being in the \"initial state\" and\n the SUT containing an initial Entity ${entity} \n with an id set to ${entityId} \n}", + "test_cases": [ + { + "name": "054_02_01 Replace an existing entity giving an invalid Id", + "permutation_tp_id": "TP/NGSI-LD/CI/Prov/E/054_02_01", + "doc": "Check that if the target entity ID is not a valid URI or it's not present a 400 error is raised", + "tags": [ + "5_6_18", + "6_5_3_3", + "e-replace", + "since_v1.6.1" + ], + "setup": "Setup Initial Entity", + "teardown": "Delete Initial Entity", + "template": "Replace an existing entity with invalid/not present ID", + "then": "then {\n the SUT sends a valid Response for the operation:\n Replace Entity Selecting Content Type with Response Status Code set to 400\n}", + "when": "when {\n the SUT receives a Request from the client containing:\n URL set to '/ngsi-ld/v1/entities/{entity_id}'\n method set to 'PUT'\n Replace Entity Selecting Content Type and\n Query Parameter: entity_id set to '${invalid_id}' and\n Query Parameter: entity_fragment set to '${entity}' and\n Query Parameter: content_type set to 'application/ld+json' and\n Query Parameter: context set to ''\n}", + "http_verb": "PUT", + "endpoint": "entities/{entity_id}" + }, + { + "name": "054_02_02 Replace an existing entity without giving an Id", + "permutation_tp_id": "TP/NGSI-LD/CI/Prov/E/054_02_02", + "doc": "Check that if the target entity ID is not a valid URI or it's not present a 400 error is raised", + "tags": [ + "5_6_18", + "6_5_3_3", + "e-replace", + "since_v1.6.1" + ], + "setup": "Setup Initial Entity", + "teardown": "Delete Initial Entity", + "template": "Replace an existing entity with invalid/not present ID", + "then": "then {\n the SUT sends a valid Response for the operation:\n Replace Entity Selecting Content Type with Response Status Code set to 400\n}", + "when": "when {\n the SUT receives a Request from the client containing:\n URL set to '/ngsi-ld/v1/entities/{entity_id}'\n method set to 'PUT'\n Replace Entity Selecting Content Type and\n Query Parameter: entity_id set to '${EMPTY}' and\n Query Parameter: entity_fragment set to '${entity}' and\n Query Parameter: content_type set to 'application/ld+json' and\n Query Parameter: context set to ''\n}", + "http_verb": "PUT", + "endpoint": "entities/{entity_id}" + } + ], + "permutations": [ + "when" + ], + "robotpath": "ContextInformation/Provision/Entities/ReplaceEntity", + "robotfile": "054_02" +} \ No newline at end of file diff --git a/doc/tests/test_ContextInformation_Provision.py b/doc/tests/test_ContextInformation_Provision.py index 338d13e637511ba4999522fd9dbb4903743ad695..3280f845e8f7c862d0c7ba07bbc8e401c911122e 100644 --- a/doc/tests/test_ContextInformation_Provision.py +++ b/doc/tests/test_ContextInformation_Provision.py @@ -657,3 +657,17 @@ class TestCIProvision(TestCase): difference_file = f'{self.folder_test_suites}/doc/results/out_016_03.json' self.common_function(robot_file=robot_file, expected_value=expected_value, difference_file=difference_file) + + def test_054_01(self): + robot_file = f'{self.folder_test_suites}/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_01.robot' + expected_value = f'{self.folder_test_suites}/doc/files/ContextInformation/Provision/054_01.json' + difference_file = f'{self.folder_test_suites}/doc/results/out_054_01.json' + + self.common_function(robot_file=robot_file, expected_value=expected_value, difference_file=difference_file) + + def test_054_02(self): + robot_file = f'{self.folder_test_suites}/TP/NGSI-LD/ContextInformation/Provision/Entities/ReplaceEntity/054_02.robot' + expected_value = f'{self.folder_test_suites}/doc/files/ContextInformation/Provision/054_02.json' + difference_file = f'{self.folder_test_suites}/doc/results/out_054_02.json' + + self.common_function(robot_file=robot_file, expected_value=expected_value, difference_file=difference_file) diff --git a/resources/ApiUtils/ContextInformationConsumption.resource b/resources/ApiUtils/ContextInformationConsumption.resource index 5edd5a87b9721fabaf1c40b262d7defe40ff85d3..6859c8e81801f647ea902c407fdc5261ee1da3ff 100755 --- a/resources/ApiUtils/ContextInformationConsumption.resource +++ b/resources/ApiUtils/ContextInformationConsumption.resource @@ -209,15 +209,24 @@ Retrieve Attributes RETURN ${response} Retrieve Entity by Id - [Arguments] ${id} ${accept}=${CONTENT_TYPE_LD_JSON} ${context}=${EMPTY} + [Arguments] ${id} ${accept}=${CONTENT_TYPE_LD_JSON} ${context}=${EMPTY} ${options}=${EMPTY} ${headers}= Create Dictionary + &{params}= Create Dictionary + ${options_length}= Get Length ${options} Set To Dictionary ${headers} Accept ${accept} IF '${context}'!='' Set To Dictionary ... ${headers} ... Link=<${context}>; rel="http://www.w3.org/ns/json-ld#context";type="application/ld+json" END - ${response}= GET url=${url}/${ENTITIES_ENDPOINT_PATH}${id} headers=${headers} expected_status=any + IF ${options_length}>0 + Set To Dictionary ${params} options=${options} + END + ${response}= GET + ... url=${url}/${ENTITIES_ENDPOINT_PATH}${id} + ... headers=${headers} + ... params=${params} + ... expected_status=any Output ${response} Retrieve Entity by Id RETURN ${response} diff --git a/resources/ApiUtils/ContextInformationProvision.resource b/resources/ApiUtils/ContextInformationProvision.resource index 8a32ca2e8278d86184f25110ba44d0ec31b01f67..5a7b1090246151539aed0de60133dfc46b94677c 100755 --- a/resources/ApiUtils/ContextInformationProvision.resource +++ b/resources/ApiUtils/ContextInformationProvision.resource @@ -265,3 +265,23 @@ Update Entity Attributes ... expected_status=any Output ${response} Update Entity Attributes RETURN ${response} + +Replace Entity Selecting Content Type + [Arguments] + ... ${entity_id} + ... ${entity_fragment} + ... ${content_type} + ... ${context}=${EMPTY} + &{headers}= Create Dictionary Content-Type=${content_type} + IF '${context}'!='' + Set To Dictionary + ... ${headers} + ... Link=<${context}>; rel="http://www.w3.org/ns/json-ld#context";type="application/ld+json" + END + ${response}= PUT + ... url=${url}/${ENTITIES_ENDPOINT_PATH}${entity_id} + ... headers=${headers} + ... json=${entity_fragment} + ... expected_status=any + Output ${response} Replace Entity Selecting Content Type + RETURN ${response}