Skip to content

Commit 31f047a

Browse files
authored
[uss_qualifier/netrid] Check response validity when calling all_flights (#1388)
1 parent 6297295 commit 31f047a

File tree

14 files changed

+163
-110
lines changed

14 files changed

+163
-110
lines changed

.basedpyright/baseline.json

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13509,30 +13509,6 @@
1350913509
"lineCount": 1
1351013510
}
1351113511
},
13512-
{
13513-
"code": "reportOptionalMemberAccess",
13514-
"range": {
13515-
"startColumn": 36,
13516-
"endColumn": 50,
13517-
"lineCount": 1
13518-
}
13519-
},
13520-
{
13521-
"code": "reportOptionalMemberAccess",
13522-
"range": {
13523-
"startColumn": 50,
13524-
"endColumn": 64,
13525-
"lineCount": 1
13526-
}
13527-
},
13528-
{
13529-
"code": "reportOptionalMemberAccess",
13530-
"range": {
13531-
"startColumn": 80,
13532-
"endColumn": 88,
13533-
"lineCount": 1
13534-
}
13535-
},
1353613512
{
1353713513
"code": "reportArgumentType",
1353813514
"range": {
@@ -13573,14 +13549,6 @@
1357313549
"lineCount": 1
1357413550
}
1357513551
},
13576-
{
13577-
"code": "reportOptionalMemberAccess",
13578-
"range": {
13579-
"startColumn": 80,
13580-
"endColumn": 88,
13581-
"lineCount": 1
13582-
}
13583-
},
1358413552
{
1358513553
"code": "reportCallIssue",
1358613554
"range": {

monitoring/uss_qualifier/scenarios/astm/netrid/common/misbehavior.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,12 @@ def poll_func(rect: LatLngRect) -> bool:
178178
poll_func,
179179
)
180180

181+
def _is_area_too_large(self, rect: s2sphere.LatLngRect) -> bool:
182+
return geo.get_latlngrect_diagonal_km(rect) > self._rid_version.max_diagonal_km
183+
181184
def _fetch_flights_from_dss(self, rect: LatLngRect) -> dict[str, TelemetryMapping]:
182185
# We grab all flights from the SPs (which we know how to reach by first querying the DSS).
183186
# This is authenticated and is expected to succeed
184-
# TODO: Add the following requests to the documentation. Possibly split it as a test step.
185187
sp_observation = rid.all_flights(
186188
rect,
187189
include_recent_positions=True,
@@ -191,14 +193,23 @@ def _fetch_flights_from_dss(self, rect: LatLngRect) -> dict[str, TelemetryMappin
191193
dss_participant_id=self._dss.participant_id,
192194
)
193195

196+
self.record_queries(sp_observation.queries)
197+
194198
mapping_by_injection_id = (
195199
display_data_evaluator.map_fetched_to_injected_flights(
196200
self._injected_flights,
197201
list(sp_observation.uss_flight_queries.values()),
202+
list(sp_observation.uss_flight_details_queries.values()),
198203
self._query_cache,
199204
)
200205
)
201-
self.record_queries(sp_observation.queries)
206+
207+
display_data_evaluator.check_fetched_flights(
208+
sp_observation,
209+
self,
210+
self._dss.participant_id,
211+
self._is_area_too_large(rect),
212+
)
202213

203214
return mapping_by_injection_id
204215

monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator.py

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from monitoring.monitorlib.fetch import Query
1717
from monitoring.monitorlib.fetch.rid import (
1818
FetchedFlights,
19+
FetchedUSSFlightDetails,
1920
FetchedUSSFlights,
2021
Position,
2122
all_flights,
@@ -41,7 +42,10 @@
4142
from monitoring.uss_qualifier.scenarios.astm.netrid.virtual_observer import (
4243
VirtualObserver,
4344
)
44-
from monitoring.uss_qualifier.scenarios.scenario import TestScenario
45+
from monitoring.uss_qualifier.scenarios.scenario import (
46+
GenericTestScenario,
47+
TestScenario,
48+
)
4549

4650
SPEED_PRECISION = 0.05
4751
HEIGHT_PRECISION_M = 1
@@ -102,8 +106,10 @@ class FetchedToInjectedCache:
102106
mappings: dict[str, ParticipantID] = field(default_factory=dict)
103107
"""Mapping is a map of URL -> ParticipantID that we found until now"""
104108

105-
unmapped: list[FetchedUSSFlights] = field(default_factory=list)
106-
"""Unmapped is the list of flights we didn't attributed yet"""
109+
unmapped: list[FetchedUSSFlights | FetchedUSSFlightDetails] = field(
110+
default_factory=list
111+
)
112+
"""Unmapped is the list of flights or flight details we didn't attributed yet"""
107113

108114

109115
def map_observations_to_injected_flights(
@@ -173,9 +179,60 @@ def map_observations_to_injected_flights(
173179
return mapping
174180

175181

182+
def check_fetched_flights(
183+
fetched_flights: FetchedFlights,
184+
scenario: GenericTestScenario,
185+
dss_participant_id: str,
186+
area_too_large: bool,
187+
):
188+
"""Perform checks on a FetchedFlights result from 'rid.all_flights' helper"""
189+
190+
with scenario.check("Successful ISA query", [dss_participant_id]) as check:
191+
if not fetched_flights.dss_isa_query.success:
192+
check.record_failed(
193+
summary="Could not query ISAs from DSS",
194+
details=f"Query to {dss_participant_id}'s DSS at failed: {', '.join(fetched_flights.dss_isa_query.errors)}",
195+
query_timestamps=[
196+
fetched_flights.dss_isa_query.request.initiated_at.datetime
197+
],
198+
)
199+
200+
if area_too_large: # Don't perform flight queries checks as they should fail
201+
return
202+
203+
for flight_query in fetched_flights.uss_flight_queries.values():
204+
with scenario.check(
205+
"Successful flight query", flight_query.participant_id
206+
) as check:
207+
if not flight_query.success:
208+
check.record_failed(
209+
summary="Flight query not successful",
210+
details=f"Query to {flight_query.query.request.url} failed: {', '.join(flight_query.errors)}",
211+
query_timestamps=[flight_query.query.request.initiated_at.datetime]
212+
if flight_query.query.request.initiated_at
213+
else [],
214+
)
215+
216+
for flight_details_query in fetched_flights.uss_flight_details_queries.values():
217+
with scenario.check(
218+
"Successful flight details query", flight_details_query.participant_id
219+
) as check:
220+
if not flight_details_query.success:
221+
check.record_failed(
222+
summary="Flight details query not successful",
223+
details=f"Query to {flight_details_query.query.request.url} failed: {', '.join(flight_details_query.errors)}",
224+
query_timestamps=[
225+
flight_details_query.query.request.initiated_at.datetime
226+
]
227+
if flight_details_query.query.request.initiated_at
228+
else [],
229+
)
230+
231+
176232
def map_fetched_to_injected_flights(
177233
injected_flights: list[InjectedFlight],
178234
fetched_flights: list[FetchedUSSFlights],
235+
fetched_flights_details: list[FetchedUSSFlightDetails],
179236
query_cache: FetchedToInjectedCache,
180237
) -> dict[str, TelemetryMapping]:
181238
"""Identify which of the fetched flights (if any) matches to each of the injected flights.
@@ -185,6 +242,7 @@ def map_fetched_to_injected_flights(
185242
186243
:param injected_flights: Flights injected into RID Service Providers under test.
187244
:param fetched_flights: Flight observed from an RID Display Provider under test.
245+
:param fetched_flights_details: Flight details observed from an RID Display Provider under test.
188246
:param query_cache: A FetchedToInjectedCache, used to maintain participant_id in various queries
189247
:return: Mapping between InjectedFlight and observed Flight, indexed by injection_id.
190248
"""
@@ -205,7 +263,7 @@ def map_fetched_to_injected_flights(
205263
)
206264

207265
# Add to the todo list queries to map
208-
for uss_query in fetched_flights:
266+
for uss_query in fetched_flights + fetched_flights_details:
209267
if not uss_query.has_field_with_value("participant_id"):
210268
query_cache.unmapped.append(uss_query)
211269

@@ -284,14 +342,23 @@ def evaluate_system_instantaneously(
284342
dss_participant_id=self._dss.participant_id,
285343
)
286344

345+
for q in sp_observation.queries:
346+
self._test_scenario.record_query(q)
347+
287348
# map observed flights to injected flight and attribute participant ID
288349
mapping_by_injection_id = map_fetched_to_injected_flights(
289350
self._injected_flights,
290351
list(sp_observation.uss_flight_queries.values()),
352+
list(sp_observation.uss_flight_details_queries.values()),
291353
self._query_cache,
292354
)
293-
for q in sp_observation.queries:
294-
self._test_scenario.record_query(q)
355+
356+
check_fetched_flights(
357+
sp_observation,
358+
self._test_scenario,
359+
self._dss.participant_id,
360+
self._is_area_too_large(rect),
361+
)
295362

296363
# Evaluate observations
297364
self._evaluate_sp_observation(rect, sp_observation, mapping_by_injection_id)
@@ -325,6 +392,9 @@ def evaluate_system_instantaneously(
325392
# TODO: If bounding rect is smaller than area-too-large threshold, expand slightly above area-too-large threshold and re-observe
326393
self._test_scenario.end_test_step()
327394

395+
def _is_area_too_large(self, rect: s2sphere.LatLngRect) -> bool:
396+
return geo.get_latlngrect_diagonal_km(rect) > self._rid_version.max_diagonal_km
397+
328398
def _evaluate_observation(
329399
self,
330400
observer: RIDSystemObserver,
@@ -333,10 +403,8 @@ def _evaluate_observation(
333403
query: fetch.Query,
334404
verified_sps: set[str],
335405
) -> None:
336-
diagonal_km = (
337-
rect.lo().get_distance(rect.hi()).degrees * geo.EARTH_CIRCUMFERENCE_KM / 360
338-
)
339-
if diagonal_km > self._rid_version.max_diagonal_km:
406+
diagonal_km = geo.get_latlngrect_diagonal_km(rect)
407+
if self._is_area_too_large(rect):
340408
self._evaluate_area_too_large_observation(
341409
observer, rect, diagonal_km, query
342410
)
@@ -755,19 +823,6 @@ def _evaluate_sp_observation(
755823
# endpoints (and therefore cannot provide a callback/base URL), calling the one-time query endpoint
756824
# is currently much cleaner. If this test is applied to a DSS that does not implement the one-time
757825
# ISA query endpoint, this check can be adapted.
758-
with self._test_scenario.check(
759-
"ISA query", [self._dss.participant_id]
760-
) as check:
761-
if not sp_observation.dss_isa_query.success:
762-
check.record_failed(
763-
summary="Could not query ISAs from DSS",
764-
details=f"Query to {self._dss.participant_id}'s DSS at {sp_observation.dss_isa_query.query.request.url} failed {sp_observation.dss_isa_query.query.status_code}",
765-
query_timestamps=[
766-
sp_observation.dss_isa_query.query.request.initiated_at.datetime
767-
],
768-
)
769-
return
770-
771826
diagonal_km = (
772827
requested_area.lo().get_distance(requested_area.hi()).degrees
773828
* geo.EARTH_CIRCUMFERENCE_KM
@@ -1070,14 +1125,23 @@ def evaluate_disconnected_flights(
10701125
dss_participant_id=self._dss.participant_id,
10711126
)
10721127

1128+
for q in sp_observation.queries:
1129+
self._test_scenario.record_query(q)
1130+
10731131
# map observed flights to injected flight and attribute participant ID
10741132
mapping_by_injection_id = map_fetched_to_injected_flights(
10751133
self._injected_flights,
10761134
list(sp_observation.uss_flight_queries.values()),
1135+
list(sp_observation.uss_flight_details_queries.values()),
10771136
self._query_cache,
10781137
)
1079-
for q in sp_observation.queries:
1080-
self._test_scenario.record_query(q)
1138+
1139+
check_fetched_flights(
1140+
sp_observation,
1141+
self._test_scenario,
1142+
self._dss.participant_id,
1143+
False,
1144+
)
10811145

10821146
# Evaluate observations
10831147
self._evaluate_sp_observation(sp_observation, mapping_by_injection_id)
@@ -1097,19 +1161,6 @@ def _evaluate_sp_observation(
10971161
# endpoints (and therefore cannot provide a callback/base URL), calling the one-time query endpoint
10981162
# is currently much cleaner. If this test is applied to a DSS that does not implement the one-time
10991163
# ISA query endpoint, this check can be adapted.
1100-
with self._test_scenario.check(
1101-
"ISA query", [self._dss.participant_id]
1102-
) as check:
1103-
if not sp_observation.dss_isa_query.success:
1104-
check.record_failed(
1105-
summary="Could not query ISAs from DSS",
1106-
details=f"Query to {self._dss.participant_id}'s DSS at {sp_observation.dss_isa_query.query.request.url} failed {sp_observation.dss_isa_query.query.status_code}",
1107-
query_timestamps=[
1108-
sp_observation.dss_isa_query.query.request.initiated_at.datetime
1109-
],
1110-
)
1111-
return
1112-
11131164
self._evaluate_sp_observation_of_disconnected_flights(sp_observation, mappings)
11141165

11151166
def _evaluate_sp_observation_of_disconnected_flights(

monitoring/uss_qualifier/scenarios/astm/netrid/v19/fragments/sp_polling_queries.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
uss_qualifier acts as a Display Provider to query Service Providers under test in this step.
44

5-
## ⚠️ ISA query check
5+
## ⚠️ Successful ISA query check
66

77
**[interuss.f3411.dss_endpoints.SearchISAs](../../../../../requirements/interuss/f3411/dss_endpoints.md)** requires a USS providing a DSS instance to implement the DSS endpoints of the OpenAPI specification. If uss_qualifier is unable to query the DSS for ISAs, this check will fail.
88

9+
## ⚠️ Successful flight query check
10+
11+
**[astm.f3411.v19.NET0710,1](../../../../../requirements/astm/f3411/v19.md)** and **[astm.f3411.v19.NET0340](../../../../../requirements/astm/f3411/v19.md) require a Service Provider to implement the GET flight endpoint. This check will fail if uss_qualifier cannot query that endpoint (specified in the ISA present in the DSS) successfully.
12+
913
## ⚠️ Successful flight details query check
1014

1115
**[astm.f3411.v19.NET0710,2](../../../../../requirements/astm/f3411/v19.md)** and **[astm.f3411.v19.NET0340](../../../../../requirements/astm/f3411/v19.md) require a Service Provider to implement the GET flight details endpoint. This check will fail if uss_qualifier cannot query that endpoint (specified in the ISA present in the DSS) successfully.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Service provider queries test step fragment
2+
3+
uss_qualifier acts as a Display Provider to query Service Providers under test in this step, without fetching details.
4+
5+
## ⚠️ Successful ISA query check
6+
7+
**[interuss.f3411.dss_endpoints.SearchISAs](../../../../../requirements/interuss/f3411/dss_endpoints.md)** requires a USS providing a DSS instance to implement the DSS endpoints of the OpenAPI specification. If uss_qualifier is unable to query the DSS for ISAs, this check will fail.
8+
9+
## ⚠️ Successful flight query check
10+
11+
**[astm.f3411.v19.NET0710,1](../../../../../requirements/astm/f3411/v19.md)** and **[astm.f3411.v19.NET0340](../../../../../requirements/astm/f3411/v19.md) require a Service Provider to implement the GET flight endpoint. This check will fail if uss_qualifier cannot query that endpoint (specified in the ISA present in the DSS) successfully.

monitoring/uss_qualifier/scenarios/astm/netrid/v19/misbehavior.md

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,26 @@ A [`DSSInstanceResource`](../../../../resources/astm/f3411/dss.py) is required f
2424

2525
## Invalid requests test case
2626

27-
### Injection test step
28-
29-
In this step, uss_qualifier injects a single nominal flight into each SP under test, usually with a start time in the future. Each SP is expected to queue the provided telemetry and later simulate that telemetry coming from an aircraft at the designated timestamps.
30-
31-
#### 🛑 Successful injection check
32-
33-
Per **[interuss.automated_testing.rid.injection.UpsertTestSuccess](../../../../requirements/interuss/automated_testing/rid/injection.md)**, the injection attempt of the valid flight should succeed for every NetRID Service Provider under test.
34-
35-
**[astm.f3411.v19.NET0500](../../../../requirements/astm/f3411/v19.md)** requires a Service Provider to provide a persistently supported test instance of their implementation.
36-
This check will fail if the flight was not successfully injected.
37-
38-
#### 🛑 Identifiable flights check
39-
40-
This particular test requires each flight to be uniquely identifiable by its 2D telemetry position; the same (lat, lng) pair may not appear in two different telemetry points, even if the two points are in different injected flights. This should generally be achieved by injecting appropriate data.
27+
### [Injection test step](./fragments/flight_injection.md)
4128

4229
### Invalid search area test step
4330

44-
This step will attempt to search for flights in a rectangular area with a diagonal greater than [NetMaxDisplayAreaDiagonal] km.
31+
This step will attempt to search for flights in a rectangular area with a diagonal greater than [NetMaxDisplayAreaDiagonal] km. First, the Service Providers with service in the large area will be determined from the DSS and then each Service Provider will be queried for flights (this should succeed). Then each Service Provider will be queried again for flights, this time using an unacceptably-large area (this should fail).
32+
33+
#### [Service provider queries test step](../v19/fragments/sp_simple_queries.md)
4534

4635
#### ⚠️ Area too large check
4736

4837
**[astm.f3411.v19.NET0250](../../../../requirements/astm/f3411/v19.md)** requires that a NetRID Service Provider rejects a request for a very large view area with a diagonal greater than *NetMaxDisplayAreaDiagonal*. If such a large view is requested and a 400 or 413 error code is not received or the response contains Remote ID data, then this check will fail.
4938

5039
### Unauthenticated requests test step
5140

52-
In order to properly test whether the SP handles authentication correctly, this step will first attempt to do a request with the proper credentials
53-
to confirm that the requested data is indeed available to any authorized query.
41+
in order to properly test whether the SP handles authentication correctly, after identifying the SP contact information via its ISA in the DSS, this step will first attempt to do a flights request with the proper credentials to confirm that the requested data is indeed available to any authorized query.
5442

5543
It then repeats the exact same request without credentials, and expects this to fail.
5644

45+
#### [Service provider queries test step](../v19/fragments/sp_simple_queries.md)
46+
5747
#### ⚠️ Missing credentials check
5848

5949
This check ensures that all requests are properly authenticated, as required by **[astm.f3411.v19.NET0210](../../../../requirements/astm/f3411/v19.md)**,
@@ -63,6 +53,8 @@ and that requests for existing flights that are executed with missing credential
6353

6454
This step is similar to unauthenticated requests, but uses incorrectly-authenticated requests instead.
6555

56+
#### [Service provider queries test step](../v19/fragments/sp_simple_queries.md)
57+
6658
#### ⚠️ Invalid credentials check
6759
This check ensures that all requests are properly authenticated, as required by **[astm.f3411.v19.NET0210](../../../../requirements/astm/f3411/v19.md)**,
6860
and that requests for existing flights that are executed with incorrect credentials fail.

0 commit comments

Comments
 (0)