From 53aee3913c341a56f6b914a53d263127e3bfd9be Mon Sep 17 00:00:00 2001 From: kzangeli Date: Thu, 4 Jun 2026 13:48:45 +0200 Subject: [PATCH] fix: temporal pagination tests follow TS 104-176 clause 6.4.7.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 020_13 / 020_14 / 021_15 / 021_16 families tested the old GS CIM 009 v1.x clause 6.3.10 binding: 206 Partial Content + Content-Range, with a broker-wide total instance cap cut at attribute boundaries. TS 104-176 clause 6.4.7.3 replaces that mechanism entirely: - status is always 200; page pointers are Link header link-values with rel="intervalafter" (the page holding LATER Attribute timestamps) and rel="intervalbefore" (EARLIER) — note the relations are time-oriented, so they swap sides between ascending (firstN) and descending (lastN) pagination - the page limit applies per Attribute: firstN (ascending), lastN (descending), offsetN to select the page - there is no total cap and no attribute-boundary cut: unsynchronized attributes paginate independently (020_14 now asserts exactly that) New keywords: Check Temporal Pagination Link / ... Link Absent (AssertionUtils); firstN/offsetN plumbed through the temporal consumption keywords. 021_16 drives pagination through temporalQ.lastN in the POST query body (new fixture entity-operations-paginated-query). Also covered: firstN+lastN together is contradictory → 400. 021_03 and 020_05 needed no changes — they already expected 200 and fail only against brokers still implementing the v1.x binding. --- .../021_15.robot | 208 +++++++----------- .../021_16.robot | 23 +- .../020_13.robot | 208 +++++++----------- .../020_14.robot | 28 ++- .../entity-operations-paginated-query.jsonld | 11 + ...oralContextInformationConsumption.resource | 12 + resources/AssertionUtils.resource | 17 ++ 7 files changed, 233 insertions(+), 274 deletions(-) create mode 100644 data/temporalEntities/entity-operations-paginated-query.jsonld diff --git a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_15.robot b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_15.robot index 40625f0d..52d3d21b 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_15.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_15.robot @@ -1,5 +1,5 @@ *** Settings *** -Documentation Check temporal pagination is applied when querying the temporal evolution of entities +Documentation Check that temporal pagination (TS 104-176, clause 6.4.7.3) is applied when querying the temporal evolution of entities Resource ${EXECDIR}/resources/ApiUtils/Common.resource Resource ${EXECDIR}/resources/ApiUtils/TemporalContextInformationConsumption.resource @@ -9,7 +9,7 @@ Resource ${EXECDIR}/resources/JsonUtils.resource Suite Setup Setup Initial Entities Suite Teardown Delete Initial Entities -Test Template Retrieve Temporal Entities +Test Template Query Temporal Entities With Pagination *** Variables *** @@ -17,49 +17,67 @@ ${first_vehicle_payload_file}= pagination/2020-01-vehicule-temporal-represe ${second_vehicle_payload_file}= 2020-09-vehicle-temporal-representation.jsonld ${timeBefore}= 2019-01-01T01:01:00Z ${timeAfter}= 2021-01-01T01:01:00Z -${lrt}= least recent timestamp -${mrt}= most recent timestamp *** Test Cases *** -021_15_01 Retrieve The Entity With lastN And Timerel Before - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - lastN=${20} expectedSize=20 timerel=before timeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} -021_15_02 Retrieve The Entity With lastN And Timerel Between - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - lastN=${20} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} -021_15_03 Retrieve The Entity With lastN And Timerel After - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - lastN=${20} expectedSize=20 timerel=after timeAt=${timeBefore} expectedRangeStart=${mrt} expectedRangeEnd=${lrt} -021_15_04 Retrieve The Entity With Timerel Before - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - timerel=before expectedSize=* timeAt=${timeAfter} expectedRangeEnd=${mrt} -021_15_05 Retrieve The Entity With Timerel Between - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -021_15_06 Retrieve The Entity With Timerel After - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - timerel=after timeAt=${timeBefore} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -021_15_07 Retrieve The Entity With temporalValues And Timerel After - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - representation=temporalValues timerel=after timeAt=${timeBefore} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -021_15_08 Retrieve The Entity With temporalValues, lastN And Timerel Between - [Tags] te-retrieve 5_7_4 6_3_10 since_v1.5.1 - representation=temporalValues lastN=${20} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} +021_15_01 Query With lastN Smaller Than The Available Instances + [Documentation] lastN paginates in descending order; the deeper page holds EARLIER timestamps → rel="intervalbefore" + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + lastN=${5} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} + ... maxInstances=5 expectBeforeOffset=5 + +021_15_02 Query The Second Page With lastN And offsetN + [Documentation] A middle page carries both pointers + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + lastN=${5} offsetN=${5} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} + ... maxInstances=5 expectBeforeOffset=10 expectAfterOffset=0 + +021_15_03 Query With firstN + [Documentation] firstN paginates in ascending order; the deeper page holds LATER timestamps → rel="intervalafter" + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + firstN=${5} timerel=after timeAt=${timeBefore} + ... maxInstances=5 expectAfterOffset=5 + +021_15_04 Query The Last Page With firstN And offsetN + [Documentation] Past the largest attribute's instance count only the backward pointer remains + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + firstN=${5} offsetN=${18} timerel=after timeAt=${timeBefore} + ... maxInstances=2 expectBeforeOffset=13 + +021_15_05 Query With lastN Covering All Instances + [Documentation] Nothing remains beyond the page — no pagination pointers + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + lastN=${20} timerel=after timeAt=${timeBefore} + ... maxInstances=20 + +021_15_06 Query With temporalValues And lastN + [Documentation] Pagination pointers are representation-independent + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + representation=temporalValues lastN=${5} timerel=after timeAt=${timeBefore} + ... expectBeforeOffset=5 + +021_15_07 Query With Both firstN And lastN + [Documentation] Ascending and descending pagination at once is contradictory + [Tags] te-query 5_7_4 6_4_7_3 since_v1.9.1 + firstN=${5} lastN=${5} timerel=after timeAt=${timeBefore} + ... expectedStatus=400 *** Keywords *** -Retrieve Temporal Entities - [Documentation] Check temporal pagination behavior on multiple entity endpoint +Query Temporal Entities With Pagination + [Documentation] Query the temporal evolution of entities and check the § 6.4.7.3 pagination behaviour [Arguments] ... ${representation}=${EMPTY} ... ${timerel}=${EMPTY} ... ${timeAt}=${EMPTY} ... ${endTimeAt}=${EMPTY} ... ${lastN}=${EMPTY} - ... ${expectedRangeStart}=${EMPTY} - ... ${expectedRangeEnd}=${EMPTY} - ... ${expectedSize}=${EMPTY} + ... ${firstN}=${EMPTY} + ... ${offsetN}=${EMPTY} + ... ${maxInstances}=${EMPTY} + ... ${expectAfterOffset}=${EMPTY} + ... ${expectBeforeOffset}=${EMPTY} + ... ${expectedStatus}=200 ${entity_types_to_be_retrieved}= Catenate SEPARATOR=, Vehicle ${response}= Query Temporal Representation Of Entities @@ -70,50 +88,41 @@ Retrieve Temporal Entities ... timeAt=${timeAt} ... endTimeAt=${endTimeAt} ... lastN=${lastN} - Check Response Status Code 206 ${response.status_code} - - ${contentRange}= Get Regexp Matches - ... ${response.headers}[Content-Range] - ... ([a-zA-Z\-]+) (.*-.*-.*)-(.*-.*-.*)\/(.*) - ... 1 - ... 2 - ... 3 - ... 4 - ${unit}= Set Variable ${contentRange}[0][0] - ${rangeStart}= Convert Date ${contentRange}[0][1] - ${rangeEnd}= Convert Date ${contentRange}[0][2] - ${size}= Set Variable ${contentRange}[0][3] - - Check Content Range Part Equal ${unit} date-time - - IF $expectedRangeStart != '' - IF $expectedRangeStart == $lrt - ${expectedRangeStart}= Get Least Recent Timestamp From Vehicles ${response.json()} - ELSE IF $expectedRangeStart == $mrt - ${expectedRangeStart}= Get Most Recent Timestamp From Vehicles ${response.json()} - ELSE - ${expectedRangeStart}= Convert Date ${expectedRangeStart} - END - Check Content Range Part Equal ${expectedRangeStart} ${rangeStart} + ... firstN=${firstN} + ... offsetN=${offsetN} + + Check Response Status Code ${expectedStatus} ${response.status_code} + IF '${expectedStatus}' != '200' RETURN + + IF '${maxInstances}' != '' and '${representation}' == '' + Check Max Instances Per Attribute ${response.json()} ${maxInstances} END - IF $expectedRangeEnd != '' - IF $expectedRangeEnd == $lrt - ${expectedRangeEnd}= Get Least Recent Timestamp From Vehicles - ... ${response.json()} - ... ${representation} - ELSE IF $expectedRangeEnd == $mrt - ${expectedRangeEnd}= Get Most Recent Timestamp From Vehicles - ... ${response.json()} - ... ${representation} - ELSE - ${expectedRangeEnd}= Convert Date ${expectedRangeEnd} - END - Check Content Range Part Equal ${expectedRangeEnd} ${rangeEnd} + IF '${expectAfterOffset}' != '' + Check Temporal Pagination Link ${response.headers} intervalafter ${expectAfterOffset} + ELSE + Check Temporal Pagination Link Absent ${response.headers} intervalafter END - IF $expectedSize != '' - Check Content Range Part Equal ${expectedSize} ${size} + IF '${expectBeforeOffset}' != '' + Check Temporal Pagination Link ${response.headers} intervalbefore ${expectBeforeOffset} + ELSE + Check Temporal Pagination Link Absent ${response.headers} intervalbefore + END + +Check Max Instances Per Attribute + [Documentation] Every Attribute of every returned Entity carries at most the page-limit number of instances + [Arguments] ${body} ${maxInstances} + FOR ${entity} IN @{body} + FOR ${key} ${value} IN &{entity} + IF $key in ('id', 'type', '@context') CONTINUE + ${isList}= Evaluate isinstance($value, list) + IF ${isList} + ${length}= Get Length ${value} + Should Be True ${length} <= ${maxInstances} + ... attribute ${key} has ${length} instances, page limit is ${maxInstances} + END + END END Setup Initial Entities @@ -135,56 +144,3 @@ Setup Initial Entities Delete Initial Entities Delete Temporal Representation Of Entity ${first_temporal_entity_representation_id} Delete Temporal Representation Of Entity ${second_temporal_entity_representation_id} - -Get Least Recent Timestamp From Vehicles - [Arguments] ${body} ${representation}=${EMPTY} - - ${attributeList}= Get Attribute List From Vehicle Body ${body} ${representation} - - ${leastRecentTimestamp}= Convert Date ${timeAfter} - FOR ${attribute} IN @{attributeList} - IF $representation == 'temporalValues' - ${attributeTime}= Convert Date ${attribute}[1] - ELSE - ${attributeTime}= Convert Date ${attribute}[observedAt] - END - - IF $leastRecentTimestamp >= $attributeTime - ${leastRecentTimestamp}= Set Variable ${attributeTime} - END - END - RETURN ${leastRecentTimestamp} - -Get Most Recent Timestamp From Vehicles - [Arguments] ${body} ${representation}=${EMPTY} - ${attributeList}= Get Attribute List From Vehicle Body ${body} ${representation} - - ${mostRecentTimestamp}= Convert Date ${timeBefore} - FOR ${attribute} IN @{attributeList} - IF $representation == 'temporalValues' - ${attributeTime}= Convert Date ${attribute}[1] - ELSE - ${attributeTime}= Convert Date ${attribute}[observedAt] - END - IF $mostRecentTimestamp <= $attributeTime - ${mostRecentTimestamp}= Set Variable ${attributeTime} - END - END - RETURN ${mostRecentTimestamp} - -Get Attribute List From Vehicle Body - [Arguments] ${body} ${representation}=${EMPTY} - IF $representation == 'temporalValues' - ${attributeList}= Combine Lists - ... ${body}[0][speed][values] - ... ${body}[0][fuelLevel][values] - ... ${body}[1][speed][values] - ... ${body}[1][fuelLevel][values] - ELSE - ${attributeList}= Combine Lists - ... ${body}[0][speed] - ... ${body}[0][fuelLevel] - ... ${body}[1][speed] - ... ${body}[1][fuelLevel] - END - RETURN ${attributeList} diff --git a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_16.robot b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_16.robot index 0a146236..70633016 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_16.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/QueryTemporalEvolutionOfEntities/021_16.robot @@ -1,5 +1,5 @@ *** Settings *** -Documentation Check temporal pagination is applied when querying the temporal evolution of entities via POST +Documentation Check that temporal pagination (TS 104-176, clause 6.4.7.3) is applied when querying the temporal evolution of entities via POST Resource ${EXECDIR}/resources/ApiUtils/Common.resource Resource ${EXECDIR}/resources/ApiUtils/TemporalContextInformationConsumption.resource @@ -19,31 +19,24 @@ ${second_vehicle_payload_file}= 2020-09-vehicle-temporal-representation.json *** Test Cases *** PAYLOAD_FILE 021_16_01 Retrieve The Entities Via Post - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - entity-operations-before-query.jsonld + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + entity-operations-paginated-query.jsonld *** Keywords *** Retrieve Temporal Entities Via Post - [Documentation] Check that temporal pagination is triggered on the post temporal query + [Documentation] Check that temporal pagination (TS 104-176, clause 6.4.7.3) is triggered on the post temporal query [Arguments] ${payload_file} ${response}= Query Temporal Representation Of Entities Via Post ... query_file_name=${payload_file} ... context=${ngsild_test_suite_context} - Check Response Status Code 206 ${response.status_code} + Check Response Status Code 200 ${response.status_code} - ${contentRange}= Get Regexp Matches - ... ${response.headers}[Content-Range] - ... ([a-zA-Z\-]+) (.*-.*-.*)-(.*-.*-.*)\/(.*) - ... 1 - ... 2 - ... 3 - ... 4 - ${unit}= Set Variable ${contentRange}[0][0] - - Check Content Range Part Equal ${unit} date-time + # temporalQ.lastN=5 against a 20-instance fixture: instances remain + # beyond the page in descending order → rel="intervalbefore" pointer. + Check Temporal Pagination Link ${response.headers} intervalbefore 5 Setup Initial Entities ${first_temporal_entity_representation_id}= Generate Random Vehicle Entity Id diff --git a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_13.robot b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_13.robot index 1e4d87ef..993f6d33 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_13.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_13.robot @@ -1,5 +1,5 @@ *** Settings *** -Documentation Check temporal pagination is applied when retrieving the temporal evolution of an entity +Documentation Check that temporal pagination (TS 104-176, clause 6.4.7.3) is applied when retrieving the temporal evolution of an entity Resource ${EXECDIR}/resources/ApiUtils/Common.resource Resource ${EXECDIR}/resources/ApiUtils/TemporalContextInformationConsumption.resource @@ -9,62 +9,79 @@ Resource ${EXECDIR}/resources/JsonUtils.resource Suite Setup Create Temporal Entity Suite Teardown Delete Initial Temporal Entity -Test Template Retrieve Temporal Entity +Test Template Retrieve Temporal Entity With Pagination *** Variables *** ${vehicle_payload_file}= pagination/2020-01-vehicule-temporal-representation-twenty-instances.jsonld ${timeBefore}= 2019-01-01T01:01:00Z ${timeAfter}= 2021-01-01T01:01:00Z -${lrt}= least recent timestamp -${mrt}= most recent timestamp *** Test Cases *** -020_13_01 Retrieve An Entity With 20 Instances On Two Attributes - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - ${EMPTY} expectedSize=* expectedRangeStart=${lrt} expectedRangeEnd=${mrt} -020_13_02 Retrieve The Entity With lastN - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - lastN=${20} expectedSize=20 expectedRangeStart=${mrt} expectedRangeEnd=${lrt} -020_13_03 Retrieve The Entity With lastN And Timerel Before - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - lastN=${20} expectedSize=20 timerel=before timeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} -020_13_04 Retrieve The Entity With lastN And Timerel Between - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - lastN=${20} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} -020_13_05 Retrieve The Entity With lastN And Timerel After - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - lastN=${20} expectedSize=20 timerel=after timeAt=${timeBefore} expectedRangeStart=${mrt} expectedRangeEnd=${lrt} -020_13_06 Retrieve The Entity With Timerel Before - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - timerel=before expectedSize=* timeAt=${timeAfter} expectedRangeEnd=${mrt} -020_13_07 Retrieve The Entity With Timerel Between - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -020_13_08 Retrieve The Entity With Timerel After - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - timerel=after timeAt=${timeBefore} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -020_13_09 Retrieve The Entity With temporalValues And Timerel After - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - representation=temporalValues timerel=after timeAt=${timeBefore} expectedRangeStart=${timeBefore} expectedRangeEnd=${mrt} -020_13_10 Retrieve The Entity With temporalValues, lastN And Timerel Between - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - representation=temporalValues lastN=${20} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} expectedRangeStart=${timeAfter} expectedRangeEnd=${lrt} +020_13_01 Retrieve The Entity With lastN Smaller Than The Available Instances + [Documentation] lastN paginates in descending order; the deeper page holds EARLIER timestamps → rel="intervalbefore" + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${5} maxInstances=5 expectBeforeOffset=5 + +020_13_02 Retrieve The Second Page With lastN And offsetN + [Documentation] A middle page carries both pointers + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${5} offsetN=${5} maxInstances=5 expectBeforeOffset=10 expectAfterOffset=0 + +020_13_03 Retrieve The Entity With firstN + [Documentation] firstN paginates in ascending order; the deeper page holds LATER timestamps → rel="intervalafter" + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + firstN=${5} maxInstances=5 expectAfterOffset=5 + +020_13_04 Retrieve The Last Page With firstN And offsetN + [Documentation] Past the attribute's instance count only the backward pointer remains + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + firstN=${5} offsetN=${18} maxInstances=2 expectBeforeOffset=13 + +020_13_05 Retrieve The Entity With lastN Covering All Instances + [Documentation] Nothing remains beyond the page — no pagination pointers + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${20} maxInstances=20 + +020_13_06 Retrieve The Entity With lastN And Timerel Before + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${5} timerel=before timeAt=${timeAfter} maxInstances=5 expectBeforeOffset=5 + +020_13_07 Retrieve The Entity With lastN And Timerel Between + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${5} timerel=between timeAt=${timeBefore} endTimeAt=${timeAfter} maxInstances=5 expectBeforeOffset=5 + +020_13_08 Retrieve The Entity With firstN And Timerel After + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + firstN=${5} timerel=after timeAt=${timeBefore} maxInstances=5 expectAfterOffset=5 + +020_13_09 Retrieve The Entity With temporalValues And lastN + [Documentation] Pagination pointers are representation-independent + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + representation=temporalValues lastN=${5} expectBeforeOffset=5 + +020_13_10 Retrieve The Entity With Both firstN And lastN + [Documentation] Ascending and descending pagination at once is contradictory + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + firstN=${5} lastN=${5} expectedStatus=400 *** Keywords *** -Retrieve Temporal Entity - [Documentation] Check temporal pagination behavior +Retrieve Temporal Entity With Pagination + [Documentation] Retrieve the temporal evolution of the entity and check the § 6.4.7.3 pagination behaviour [Arguments] ... ${representation}=${EMPTY} ... ${timerel}=${EMPTY} ... ${timeAt}=${EMPTY} ... ${endTimeAt}=${EMPTY} ... ${lastN}=${EMPTY} - ... ${expectedRangeStart}=${EMPTY} - ... ${expectedRangeEnd}=${EMPTY} - ... ${expectedSize}=${EMPTY} + ... ${firstN}=${EMPTY} + ... ${offsetN}=${EMPTY} + ... ${maxInstances}=${EMPTY} + ... ${expectAfterOffset}=${EMPTY} + ... ${expectBeforeOffset}=${EMPTY} + ... ${expectedStatus}=200 ${response}= Retrieve Temporal Representation Of Entity ... temporal_entity_representation_id=${temporal_entity_representation_id} ... options=${representation} @@ -73,50 +90,39 @@ Retrieve Temporal Entity ... timeAt=${timeAt} ... endTimeAt=${endTimeAt} ... lastN=${lastN} - Check Response Status Code 206 ${response.status_code} - - ${contentRange}= Get Regexp Matches - ... ${response.headers}[Content-Range] - ... ([a-zA-Z\-]+) (.*-.*-.*)-(.*-.*-.*)\/(.*) - ... 1 - ... 2 - ... 3 - ... 4 - ${unit}= Set Variable ${contentRange}[0][0] - ${rangeStart}= Convert Date ${contentRange}[0][1] - ${rangeEnd}= Convert Date ${contentRange}[0][2] - ${size}= Set Variable ${contentRange}[0][3] - - Check Content Range Part Equal ${unit} date-time - - IF $expectedRangeStart != '' - IF $expectedRangeStart == $lrt - ${expectedRangeStart}= Get Least Recent Timestamp From Vehicle Body ${response.json()} - ELSE IF $expectedRangeStart == $mrt - ${expectedRangeStart}= Get Most Recent Timestamp From Vehicle Body ${response.json()} - ELSE - ${expectedRangeStart}= Convert Date ${expectedRangeStart} - END - Check Content Range Part Equal ${expectedRangeStart} ${rangeStart} + ... firstN=${firstN} + ... offsetN=${offsetN} + + Check Response Status Code ${expectedStatus} ${response.status_code} + IF '${expectedStatus}' != '200' RETURN + + IF '${maxInstances}' != '' and '${representation}' == '' + Check Max Instances Per Entity Attribute ${response.json()} ${maxInstances} END - IF $expectedRangeEnd != '' - IF $expectedRangeEnd == $lrt - ${expectedRangeEnd}= Get Least Recent Timestamp From Vehicle Body - ... ${response.json()} - ... ${representation} - ELSE IF $expectedRangeEnd == $mrt - ${expectedRangeEnd}= Get Most Recent Timestamp From Vehicle Body - ... ${response.json()} - ... ${representation} - ELSE - ${expectedRangeEnd}= Convert Date ${expectedRangeEnd} - END - Check Content Range Part Equal ${expectedRangeEnd} ${rangeEnd} + IF '${expectAfterOffset}' != '' + Check Temporal Pagination Link ${response.headers} intervalafter ${expectAfterOffset} + ELSE + Check Temporal Pagination Link Absent ${response.headers} intervalafter END - IF $expectedSize != '' - Check Content Range Part Equal ${expectedSize} ${size} + IF '${expectBeforeOffset}' != '' + Check Temporal Pagination Link ${response.headers} intervalbefore ${expectBeforeOffset} + ELSE + Check Temporal Pagination Link Absent ${response.headers} intervalbefore + END + +Check Max Instances Per Entity Attribute + [Documentation] Every Attribute of the returned Entity carries at most the page-limit number of instances + [Arguments] ${entity} ${maxInstances} + FOR ${key} ${value} IN &{entity} + IF $key in ('id', 'type', '@context') CONTINUE + ${isList}= Evaluate isinstance($value, list) + IF ${isList} + ${length}= Get Length ${value} + Should Be True ${length} <= ${maxInstances} + ... attribute ${key} has ${length} instances, page limit is ${maxInstances} + END END Create Temporal Entity @@ -130,47 +136,3 @@ Create Temporal Entity Delete Initial Temporal Entity Delete Temporal Representation Of Entity ${temporal_entity_representation_id} - -Get Least Recent Timestamp From Vehicle Body - [Arguments] ${body} ${representation}=${EMPTY} - ${attributeList}= Get Attribute List From Vehicle Body ${body} ${representation} - - ${leastRecentTimestamp}= Convert Date ${timeAfter} - FOR ${attribute} IN @{attributeList} - IF $representation == 'temporalValues' - ${attributeTime}= Convert Date ${attribute}[1] - ELSE - ${attributeTime}= Convert Date ${attribute}[observedAt] - END - - IF $leastRecentTimestamp >= $attributeTime - ${leastRecentTimestamp}= Set Variable ${attributeTime} - END - END - RETURN ${leastRecentTimestamp} - -Get Most Recent Timestamp From Vehicle Body - [Arguments] ${body} ${representation}=${EMPTY} - ${attributeList}= Get Attribute List From Vehicle Body ${body} ${representation} - - ${mostRecentTimestamp}= Convert Date ${timeBefore} - FOR ${attribute} IN @{attributeList} - IF $representation == 'temporalValues' - ${attributeTime}= Convert Date ${attribute}[1] - ELSE - ${attributeTime}= Convert Date ${attribute}[observedAt] - END - IF $mostRecentTimestamp <= $attributeTime - ${mostRecentTimestamp}= Set Variable ${attributeTime} - END - END - RETURN ${mostRecentTimestamp} - -Get Attribute List From Vehicle Body - [Arguments] ${body} ${representation}=${EMPTY} - IF $representation == 'temporalValues' - ${attributeList}= Combine Lists ${body}[speed][values] ${body}[fuelLevel][values] - ELSE - ${attributeList}= Combine Lists ${body}[speed] ${body}[fuelLevel] - END - RETURN ${attributeList} diff --git a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_14.robot b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_14.robot index 63238b65..b1b8b0c0 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_14.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/TemporalEntity/RetrieveTemporalEvolutionOfEntity/020_14.robot @@ -1,5 +1,5 @@ *** Settings *** -Documentation Check that the time range cut before the second attribute to avoid missing data in content-range +Documentation Check that unsynchronized attributes paginate independently (TS 104-176, clause 6.4.7.3: the page limit applies per Attribute) Resource ${EXECDIR}/resources/ApiUtils/Common.resource Resource ${EXECDIR}/resources/ApiUtils/TemporalContextInformationConsumption.resource @@ -19,29 +19,37 @@ ${timeAfter}= 2021-01-01T01:01:00Z *** Test Cases *** -020_14_01 Retrieve An Entity With 60 Instances Of Unsynchronized Attributes - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - timerel=after timeAt=${timeBefore} emptyAttr=fuelLevel -020_14_02 Retrieve The Entity With lastN - [Tags] te-retrieve 5_7_3 6_3_10 since_v1.5.1 - lastN=${100} timerel=before timeAt=${timeAfter} emptyAttr=speed +020_14_01 Retrieve An Entity With Unsynchronized Attributes And firstN + [Documentation] Both attributes return their own first page even though their time ranges do not overlap + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + firstN=${30} timerel=after timeAt=${timeBefore} expectedInstances=30 +020_14_02 Retrieve An Entity With Unsynchronized Attributes And lastN + [Documentation] Both attributes return their own last page even though their time ranges do not overlap + [Tags] te-retrieve 5_7_3 6_4_7_3 since_v1.9.1 + lastN=${30} timerel=before timeAt=${timeAfter} expectedInstances=30 *** Keywords *** Retrieve Temporal Entity - [Documentation] Check that the time range cut before the second attribute + [Documentation] Check that the per-attribute page limit applies to each unsynchronized attribute independently [Arguments] ... ${timerel} ... ${timeAt} - ... ${emptyAttr} + ... ${expectedInstances} ... ${lastN}=${EMPTY} + ... ${firstN}=${EMPTY} ${response}= Retrieve Temporal Representation Of Entity ... temporal_entity_representation_id=${temporal_entity_representation_id} ... context=${ngsild_test_suite_context} ... timerel=${timerel} ... timeAt=${timeAt} ... lastN=${lastN} - Check Data Is Empty ${response.json()}[${emptyAttr}] + ... firstN=${firstN} + Check Response Status Code 200 ${response.status_code} + ${speedCount}= Get Length ${response.json()}[speed] + ${fuelCount}= Get Length ${response.json()}[fuelLevel] + Should Be Equal As Integers ${speedCount} ${expectedInstances} + Should Be Equal As Integers ${fuelCount} ${expectedInstances} Create Temporal Entity ${temporal_entity_representation_id}= Generate Random Vehicle Entity Id diff --git a/data/temporalEntities/entity-operations-paginated-query.jsonld b/data/temporalEntities/entity-operations-paginated-query.jsonld new file mode 100644 index 00000000..e0bfaa65 --- /dev/null +++ b/data/temporalEntities/entity-operations-paginated-query.jsonld @@ -0,0 +1,11 @@ +{ + "type": "Query", + "entities": [{ + "type": "Vehicle" + }], + "temporalQ": { + "timerel":"before", + "timeAt":"2020-08-02T12:05:00Z", + "lastN": 5 + } +} diff --git a/resources/ApiUtils/TemporalContextInformationConsumption.resource b/resources/ApiUtils/TemporalContextInformationConsumption.resource index 95f22cf5..445d0935 100755 --- a/resources/ApiUtils/TemporalContextInformationConsumption.resource +++ b/resources/ApiUtils/TemporalContextInformationConsumption.resource @@ -40,6 +40,8 @@ Query Temporal Representation Of Entities ... ${attrs}=${EMPTY} ... ${limit}=${EMPTY} ... ${lastN}=${EMPTY} + ... ${firstN}=${EMPTY} + ... ${offsetN}=${EMPTY} ... ${accept}=${EMPTY} ... ${options}=${EMPTY} ... ${format}=${EMPTY} @@ -79,6 +81,8 @@ Query Temporal Representation Of Entities END IF ${attrs_length}>0 Set To Dictionary ${params} attrs=${attrs} IF $lastN!='' Set To Dictionary ${params} lastN=${lastN} + IF $firstN!='' Set To Dictionary ${params} firstN=${firstN} + IF $offsetN!='' Set To Dictionary ${params} offsetN=${offsetN} IF $entity_id_pattern!='' Set To Dictionary ${params} idPattern=${entity_id_pattern} END @@ -163,6 +167,8 @@ Query Temporal Representation Of Entities With Return ... ${attrs}=${EMPTY} ... ${limit}=${EMPTY} ... ${lastN}=${EMPTY} + ... ${firstN}=${EMPTY} + ... ${offsetN}=${EMPTY} ... ${accept}=${EMPTY} ${entity_types_length}= Get Length ${entity_types} ${entity_ids_length}= Get Length ${entity_ids} @@ -188,6 +194,8 @@ Query Temporal Representation Of Entities With Return IF '${timeAt}'!='' Set To Dictionary ${params} timeAt=${timeAt} IF ${attrs_length}>0 Set To Dictionary ${params} attrs=${attrs} IF '${lastN}'!='' Set To Dictionary ${params} lastN=${lastN} + IF '${firstN}'!='' Set To Dictionary ${params} firstN=${firstN} + IF '${offsetN}'!='' Set To Dictionary ${params} offsetN=${offsetN} IF '${entity_id_pattern}'!='' Set To Dictionary ${params} idPattern=${entity_id_pattern} END @@ -226,6 +234,8 @@ Retrieve Temporal Representation Of Entity ... ${timeAt}=${EMPTY} ... ${endTimeAt}=${EMPTY} ... ${lastN}=${EMPTY} + ... ${firstN}=${EMPTY} + ... ${offsetN}=${EMPTY} ... ${accept}=${EMPTY} ... ${aggrMethods}=${EMPTY} ... ${aggrPeriodDuration}=${EMPTY} @@ -258,6 +268,8 @@ Retrieve Temporal Representation Of Entity Set To Dictionary ${params} endTimeAt=${endTimeAt} END IF '${lastN}'!='' Set To Dictionary ${params} lastN=${lastN} + IF '${firstN}'!='' Set To Dictionary ${params} firstN=${firstN} + IF '${offsetN}'!='' Set To Dictionary ${params} offsetN=${offsetN} IF '${aggrMethods}'!='' Set To Dictionary ${params} aggrMethods=${aggrMethods} END diff --git a/resources/AssertionUtils.resource b/resources/AssertionUtils.resource index 6b75603d..34a3feac 100755 --- a/resources/AssertionUtils.resource +++ b/resources/AssertionUtils.resource @@ -795,3 +795,20 @@ Check Body With Alternatives IF ${result} BREAK END Should Be True ${result} + +Check Temporal Pagination Link + [Documentation] TS 104-176 § 6.4.7.3: assert a Link page pointer with the + ... given relation ("intervalafter" or "intervalbefore") is present and + ... carries the expected offsetN value in its URI-reference. + [Arguments] ${headers} ${rel} ${expectedOffsetN} + Should Contain ${headers}[Link] rel="${rel}" + ${matches}= Get Regexp Matches ${headers}[Link] <[^>]*[?&]offsetN=(\\d+)[^>]*>;rel="${rel}" 1 + Should Not Be Empty ${matches} no offsetN in the ${rel} Link URI-reference + Should Be Equal As Integers ${matches}[0] ${expectedOffsetN} + +Check Temporal Pagination Link Absent + [Documentation] TS 104-176 § 6.4.7.3: assert NO Link page pointer with the + ... given relation is present (single page / edge of the result set). + [Arguments] ${headers} ${rel} + ${link}= Get From Dictionary ${headers} Link default=${EMPTY} + Should Not Contain ${link} rel="${rel}" -- GitLab