From d4a2cc3d2d8b3746703192935ec92a9f0ab57be6 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Wed, 27 Dec 2023 18:18:03 +0000 Subject: [PATCH] Clean uss_qualifier op intents at prep time --- .../resources/astm/f3548/v21/dss.py | 13 +++-- .../scenarios/astm/utm/dss/__init__.py | 0 .../astm/utm/dss/remove_op_intent.md | 7 +++ .../astm/utm/dss/test_step_fragments.py | 28 ++++++++++ .../scenarios/astm/utm/prep_planners.md | 27 ++++++++- .../scenarios/astm/utm/prep_planners.py | 55 +++++++++++++++++-- 6 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 monitoring/uss_qualifier/scenarios/astm/utm/dss/__init__.py create mode 100644 monitoring/uss_qualifier/scenarios/astm/utm/dss/remove_op_intent.md create mode 100644 monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py diff --git a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py index f9889b6d12..3620cdf3f5 100644 --- a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py +++ b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py @@ -259,12 +259,15 @@ def delete_op_intent( if query.status_code != 200: return None, None, query else: - result = ChangeOperationalIntentReferenceResponse( - ImplicitDict.parse( - query.response.json, ChangeOperationalIntentReferenceResponse + try: + result = ChangeOperationalIntentReferenceResponse( + ImplicitDict.parse( + query.response.json, ChangeOperationalIntentReferenceResponse + ) ) - ) - return result.operational_intent_reference, result.subscribers, query + return result.operational_intent_reference, result.subscribers, query + except ValueError as e: + return None, None, query def set_uss_availability( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/remove_op_intent.md b/monitoring/uss_qualifier/scenarios/astm/utm/dss/remove_op_intent.md new file mode 100644 index 0000000000..0d9030805c --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/remove_op_intent.md @@ -0,0 +1,7 @@ +# Remove operational intent test step fragment + +This test step fragment attempts to remove from the DSS a specific operational intent reference managed by a user whose credentials are provided to uss_qualifier. + +## 🛑 Operational intent reference removed check + +If the operational intent reference could not be removed, the DSS instance used does not meet **[astm.f3548.v21.DSS0005,1](../../../../../../requirements/astm/f3548/v21.md)** diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py new file mode 100644 index 0000000000..81bb46bb51 --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py @@ -0,0 +1,28 @@ +from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance +from monitoring.uss_qualifier.scenarios.scenario import TestScenarioType +from uas_standards.astm.f3548.v21.api import EntityID + + +def remove_op_intent( + scenario: TestScenarioType, dss: DSSInstance, oi_id: EntityID, ovn: str +) -> None: + """Remove the specified operational intent reference from the DSS. + + The specified operational intent reference must be managed by `dss`'s auth adapter subscriber. + + This function implements the test step fragment described in remove_op_intent.md. + """ + removed_ref, subscribers_to_notify, query = dss.delete_op_intent(oi_id, ovn) + scenario.record_query(query) + + with scenario.check( + "Operational intent reference removed", dss.participant_id + ) as check: + if removed_ref is None: + check.record_failed( + summary=f"Could not remove op intent reference {oi_id}", + details=f"When attempting to remove op intent reference {oi_id} from the DSS, received {query.status_code}", + query_timestamps=[query.request.timestamp], + ) + + # TODO: Attempt to notify subscribers diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.md b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.md index e7c61aace3..105eab423d 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.md @@ -34,7 +34,7 @@ FlightIntentsResource containing flight intents that will be used in subsequent (Optional) If more than one FlightIntentsResource will be used in subsequent tests, additional intents may be specified with this resource. -## Preparation test case +## Flight planners preparation test case ### Check for flight planning readiness test step @@ -70,6 +70,29 @@ If the DSS fails to reply to a query concerning operational intent references in an operational intent from its own creator, it is in violation of **[astm.f3548.v21.DSS0005,1](../../../requirements/astm/f3548/v21.md)** or **[astm.f3548.v21.DSS0005,2](../../../requirements/astm/f3548/v21.md)**, and this check will fail. +#### 🛑 Area is clear of foreign op intents check + +If operational intents from foreign (non-uss_qualifier) users remain in the 4D area(s) following the preceding area clearing, then the current state of the test environment is not suitable to conduct tests so this check will fail. + +## uss_qualifier preparation test case + +In addition to foreign flight planners, uss_qualifier may have left operational intents in the DSS from an incomplete previous run. This test case attempts to clean them up if they exist. If there are no operational intents from uss_qualifier in the flight intent areas, this test case will be skipped. + +### Remove uss_qualifier op intents test step + +#### [Remove op intents](./dss/remove_op_intent.md) + +The operational intent references managed by uss_qualifier discovered in the previous test case are removed. + +### Clear area validation test step + +uss_qualifier verifies with the DSS that there are no operational intents remaining in the area. + +#### 🛑 DSS responses check + +If the DSS fails to reply to a query concerning operational intent references in a given area, it is in violation of **[astm.f3548.v21.DSS0005,1](../../../requirements/astm/f3548/v21.md)** +or **[astm.f3548.v21.DSS0005,2](../../../requirements/astm/f3548/v21.md)**, and this check will fail. + #### 🛑 Area is clear check -If operational intents remain in the 4D area(s) following the preceding area clearing, then the current state of the test environment is not suitable to conduct tests so this check will fail. +If any operational intents remain in the 4D area(s) following the preceding area clearing, then the current state of the test environment is not suitable to conduct tests so this check will fail. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py index 9a87b87273..7d4d09edea 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, List from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance @@ -6,12 +6,18 @@ FlightPlannersResource, FlightIntentsResource, ) +from monitoring.uss_qualifier.scenarios.astm.utm.dss.test_step_fragments import ( + remove_op_intent, +) from monitoring.uss_qualifier.scenarios.flight_planning.prep_planners import ( PrepareFlightPlanners as GenericPrepareFlightPlanners, ) from monitoring.uss_qualifier.resources.interuss.mock_uss.client import ( MockUSSResource, ) +from uas_standards.astm.f3548.v21.api import ( + OperationalIntentReference, +) class PrepareFlightPlanners(GenericPrepareFlightPlanners): @@ -39,7 +45,8 @@ def __init__( def run(self, context): self.begin_test_scenario(context) - self.begin_test_case("Preparation") + + self.begin_test_case("Flight planners preparation") self.begin_test_step("Check for flight planning readiness") self._check_readiness() @@ -50,13 +57,32 @@ def run(self, context): self.end_test_step() self.begin_test_step("Clear area validation") - self._validate_clear_area() + remaining_op_intents = self._validate_clear_area( + "Area is clear of foreign op intents", ignore_self=True + ) self.end_test_step() self.end_test_case() + + if remaining_op_intents: + self.begin_test_case("uss_qualifier preparation") + + self.begin_test_step("Remove uss_qualifier op intents") + self._remove_my_op_intents(remaining_op_intents) + self.end_test_step() + + self.begin_test_step("Clear area validation") + self._validate_clear_area("Area is clear", ignore_self=False) + self.end_test_step() + + self.end_test_case() + self.end_test_scenario() - def _validate_clear_area(self): + def _validate_clear_area( + self, check_name: str, ignore_self: bool + ) -> List[OperationalIntentReference]: + found_intents = [] for area in self.areas: with self.check("DSS responses", [self.dss.participant_id]) as check: try: @@ -73,7 +99,14 @@ def _validate_clear_area(self): details="See query", query_timestamps=[query.request.timestamp], ) - with self.check("Area is clear") as check: + found_intents.extend(op_intents) + + with self.check(check_name) as check: + if ignore_self: + uss_qualifier_sub = self.dss.client.auth_adapter.get_sub() + op_intents = [ + oi for oi in op_intents if oi.manager != uss_qualifier_sub + ] if op_intents: summary = f"{len(op_intents)} operational intent{'s' if len(op_intents) > 1 else ''} found in cleared area" details = ( @@ -87,3 +120,15 @@ def _validate_clear_area(self): details=details, query_timestamps=[query.request.timestamp], ) + + return found_intents + + def _remove_my_op_intents( + self, my_op_intents: List[OperationalIntentReference] + ) -> None: + already_removed = set() + for oi_ref in my_op_intents: + if oi_ref.id in already_removed: + continue + remove_op_intent(self, self.dss, oi_ref.id, oi_ref.ovn) + already_removed.add(oi_ref.id)