Skip to content

Commit 507d26c

Browse files
Merge remote-tracking branch 'interuss/main' into netrid-testing-visualization
2 parents f5de6b1 + 902b1d8 commit 507d26c

File tree

20 files changed

+259
-41
lines changed

20 files changed

+259
-41
lines changed

monitoring/monitorlib/inspection.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import inspect
33
import pkgutil
44

5+
_modules_imported = set()
6+
57

68
def import_submodules(module) -> None:
79
"""Ensure that all descendant modules of a module are loaded.
@@ -10,10 +12,13 @@ def import_submodules(module) -> None:
1012
1113
:param module: Parent module from which to start explicitly importing modules.
1214
"""
15+
if module in _modules_imported:
16+
return
1317
for loader, module_name, is_pkg in pkgutil.walk_packages(
1418
module.__path__, module.__name__ + "."
1519
):
1620
importlib.import_module(module_name)
21+
_modules_imported.add(module)
1722

1823

1924
def get_module_object_by_name(parent_module, object_name: str):

monitoring/uss_qualifier/configurations/configuration.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,24 +168,13 @@ class FullyQualifiedCheck(ImplicitDict):
168168
"""Scenario in which the check occurs."""
169169

170170
test_case_name: str
171-
"""Test case in which the check occurs."""
171+
"""Test case in which the check occurs, omitting the ' test case' suffix. Must be an exact match to documentation; sensitive to case and spacing."""
172172

173173
test_step_name: str
174-
"""Test step in which the check occurs."""
174+
"""Test step in which the check occurs, omitting the ' test step' suffix. Must be an exact match to documentation; sensitive to case and spacing."""
175175

176176
check_name: str
177-
"""Name of the check."""
178-
179-
def contained_in(self, collection: Iterable[FullyQualifiedCheck]) -> bool:
180-
for other in collection:
181-
if (
182-
self.scenario_type == other.scenario_type
183-
and self.test_case_name == other.test_case_name
184-
and self.test_step_name == other.test_step_name
185-
and self.check_name == other.check_name
186-
):
187-
return True
188-
return False
177+
"""Name of the check, omitting the ' check' suffix. Must be an exact match to documentation; sensitive to case and spacing."""
189178

190179

191180
class TestedRequirementsConfiguration(ImplicitDict):

monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ v1:
417417

418418
# It is acceptable for the checks listed below to fail (in terms of determining the status of tested requirements in this artifact)
419419
acceptable_findings:
420-
- scenario_type: scenarios.astm.utm.dss.DSSInteroperability
420+
- scenario_type: scenarios.astm.utm.dss.dss_interoperability.DSSInteroperability
421421
test_case_name: "Prerequisites"
422422
test_step_name: "Test environment requirements"
423423
check_name: "DSS instance is publicly addressable"

monitoring/uss_qualifier/reports/tested_requirements/breakdown.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
from monitoring.uss_qualifier.requirements.definitions import RequirementID
3838
from monitoring.uss_qualifier.scenarios.definitions import TestScenarioTypeName
3939
from monitoring.uss_qualifier.scenarios.documentation.parsing import get_documentation
40-
from monitoring.uss_qualifier.scenarios.scenario import get_scenario_type_by_name
40+
from monitoring.uss_qualifier.scenarios.scenario import (
41+
are_scenario_types_equal,
42+
fully_qualified_check_in_collection,
43+
get_scenario_type_by_name,
44+
)
4145
from monitoring.uss_qualifier.suites.definitions import (
4246
ActionType,
4347
TestSuiteActionDeclaration,
@@ -188,7 +192,7 @@ def _populate_breakdown_with_scenario_report(
188192
matches = [
189193
s
190194
for s in tested_requirement.scenarios
191-
if s.type == scenario_type_name
195+
if are_scenario_types_equal(s.type, scenario_type_name)
192196
]
193197
if matches:
194198
tested_scenario = matches[0]
@@ -237,8 +241,8 @@ def _populate_breakdown_with_scenario_report(
237241
name=check.name,
238242
url="",
239243
has_todo=False,
240-
is_finding_acceptable=current_check.contained_in(
241-
acceptable_findings
244+
is_finding_acceptable=fully_qualified_check_in_collection(
245+
current_check, acceptable_findings
242246
),
243247
) # TODO: Consider populating has_todo with documentation instead
244248
if isinstance(check, FailedCheck):
@@ -340,7 +344,7 @@ def _populate_breakdown_with_scenario(
340344
matches = [
341345
s
342346
for s in tested_requirement.scenarios
343-
if s.type == scenario_type_name
347+
if are_scenario_types_equal(s.type, scenario_type_name)
344348
]
345349
if matches:
346350
tested_scenario = matches[0]
@@ -383,8 +387,8 @@ def _populate_breakdown_with_scenario(
383387
name=check.name,
384388
url=check.url,
385389
has_todo=check.has_todo,
386-
is_finding_acceptable=current_check.contained_in(
387-
acceptable_findings
390+
is_finding_acceptable=fully_qualified_check_in_collection(
391+
current_check, acceptable_findings
388392
),
389393
)
390394
tested_step.checks.append(tested_check)

monitoring/uss_qualifier/scenarios/astm/utm/dss/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@
2020
)
2121
from .subscription_simple import SubscriptionSimple as SubscriptionSimple
2222
from .subscription_validation import SubscriptionValidation as SubscriptionValidation
23+
from .uss_availability_mutation import (
24+
UssAvailabilityMutation as UssAvailabilityMutation,
25+
)

monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/availability/read.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# USS Availability Read test step fragment
22

3-
This fragment contains the steps for the USS Availability synchronization scenario
3+
This fragment contains the steps for the USS Availability scenario
44
where we confirm that a USS availability can be correctly read from a DSS instance
55

66
## 🛑 USS Availability can be requested check

monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/availability/update.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# USS Availability Read test step fragment
1+
# USS Availability Update test step fragment
22

3-
This fragment contains the steps for the USS Availability synchronization scenario
3+
This fragment contains the steps for the USS Availability scenario
44
where we confirm that a USS availability can be correctly read from a DSS instance
55

66
## 🛑 USS Availability can be updated check
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# ASTM Availability DSS: USS Availability Mutation test scenario
2+
3+
## Overview
4+
5+
Verifies the behavior of a DSS for simple interactions pertaining to USS availability status.
6+
7+
## Resources
8+
9+
### dss
10+
11+
[`DSSInstanceResource`](../../../../resources/astm/f3548/v21/dss.py) the DSS instance through which entities are created, modified and deleted.
12+
13+
### client_identity
14+
15+
[`ClientIdentityResource`](../../../../resources/communications/client_identity.py) the client identity with the `utm.availability_arbitration` scope that will be used to report the availability status.
16+
17+
## Update USS availability state test case
18+
19+
### Declare USS as available at DSS test step
20+
21+
#### [Availability can be read](./fragments/availability/read.md)
22+
23+
#### [Availability can be updated](./fragments/availability/update.md)
24+
25+
## Update requires correct version test case
26+
27+
Test DSS behavior when update requests are not providing the required version.
28+
29+
### Attempt update with missing version test step
30+
31+
This step verifies that an existing USS availability status cannot be mutated with a missing version.
32+
33+
#### 🛑 Request to update USS availability status with empty version fails check
34+
35+
If the DSS under test allows the qualifier to update the USS availability status with a request that provided an empty version, it is in violation of **[astm.f3548.v21.DSS0100,1](../../../../requirements/astm/f3548/v21.md)**
36+
37+
### Attempt update with incorrect version test step
38+
39+
This step verifies that an existing OIR cannot be mutated with an incorrect version.
40+
41+
#### 🛑 Request to update USS availability status with incorrect version fails check
42+
43+
If the DSS under test allows the qualifier to update the USS availability status with a request that provided an incorrect version,
44+
it is in violation of **[astm.f3548.v21.DSS0100,1](../../../../requirements/astm/f3548/v21.md)**
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from uas_standards.astm.f3548.v21.api import UssAvailabilityState
2+
from uas_standards.astm.f3548.v21.constants import Scope
3+
4+
from monitoring.monitorlib.fetch import QueryError
5+
from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import (
6+
DSSInstance,
7+
DSSInstanceResource,
8+
)
9+
from monitoring.uss_qualifier.resources.communications import ClientIdentityResource
10+
from monitoring.uss_qualifier.scenarios.astm.utm.dss.test_step_fragments import (
11+
get_uss_availability,
12+
set_uss_availability,
13+
)
14+
from monitoring.uss_qualifier.scenarios.scenario import TestScenario
15+
from monitoring.uss_qualifier.suites.suite import ExecutionContext
16+
17+
18+
class UssAvailabilityMutation(TestScenario):
19+
"""
20+
A scenario that verifies that USS availability status cannot be updated with the incorrect version.
21+
"""
22+
23+
_dss: DSSInstance
24+
_pid: list[str]
25+
26+
_uss_id: str
27+
28+
def __init__(
29+
self,
30+
dss: DSSInstanceResource,
31+
client_identity: ClientIdentityResource,
32+
):
33+
"""
34+
Args:
35+
dss: dss to test
36+
client_identity: tells us the identity we should expect as an entity's manager
37+
"""
38+
super().__init__()
39+
scopes: dict[str, str] = {
40+
Scope.AvailabilityArbitration: "read and set availability for a USS"
41+
}
42+
43+
self._dss = dss.get_instance(scopes)
44+
self._pid = [self._dss.participant_id]
45+
46+
self._uss_id = client_identity.subject()
47+
48+
def run(self, context: ExecutionContext):
49+
self.begin_test_scenario(context)
50+
51+
self.begin_test_case("Update USS availability state")
52+
self._step_declare_uss_available()
53+
self.end_test_case()
54+
55+
self.begin_test_case("Update requires correct version")
56+
self._step_attempt_update_missing_version()
57+
self._step_attempt_update_incorrect_version()
58+
self.end_test_case()
59+
60+
def _step_declare_uss_available(self):
61+
self.begin_test_step("Declare USS as available at DSS")
62+
_, version = get_uss_availability(
63+
self,
64+
self._dss,
65+
self._uss_id,
66+
Scope.AvailabilityArbitration,
67+
)
68+
set_uss_availability(
69+
self, self._dss, self._uss_id, UssAvailabilityState.Normal, version
70+
)
71+
self.end_test_step()
72+
73+
def _step_attempt_update_missing_version(self):
74+
self.begin_test_step("Attempt update with missing version")
75+
with self.check(
76+
"Request to update USS availability status with empty version fails",
77+
self._pid,
78+
) as check:
79+
try:
80+
_, q = self._dss.set_uss_availability(
81+
self._uss_id,
82+
UssAvailabilityState.Down,
83+
"",
84+
)
85+
self.record_query(q)
86+
# We don't expect the reach this point:
87+
check.record_failed(
88+
summary="Set USS availability with missing version was not expected to succeed",
89+
details=f"Was expecting an HTTP 409 response because of an missing version, but got {q.status_code} instead",
90+
query_timestamps=[q.request.timestamp],
91+
)
92+
except QueryError as qe:
93+
self.record_queries(qe.queries)
94+
if qe.cause_status_code != 409:
95+
check.record_failed(
96+
summary="Set USS availability with missing version failed for unexpected reason",
97+
details=f"Was expecting an HTTP 409 response because of an missing version, but got {qe.cause_status_code} instead",
98+
query_timestamps=qe.query_timestamps,
99+
)
100+
self.end_test_step()
101+
102+
def _step_attempt_update_incorrect_version(self):
103+
self.begin_test_step("Attempt update with incorrect version")
104+
with self.check(
105+
"Request to update USS availability status with incorrect version fails",
106+
self._pid,
107+
) as check:
108+
try:
109+
_, q = self._dss.set_uss_availability(
110+
self._uss_id,
111+
UssAvailabilityState.Down,
112+
"ThisIsAnIncorrectVersion",
113+
)
114+
self.record_query(q)
115+
# We don't expect the reach this point:
116+
check.record_failed(
117+
summary="Set USS availability with incorrect version was not expected to succeed",
118+
details=f"Was expecting an HTTP 409 response because of an incorrect version, but got {q.status_code} instead",
119+
query_timestamps=[q.request.timestamp],
120+
)
121+
except QueryError as qe:
122+
self.record_queries(qe.queries)
123+
if qe.cause_status_code != 409:
124+
check.record_failed(
125+
summary="Set USS availability with incorrect version failed for unexpected reason",
126+
details=f"Was expecting an HTTP 409 response because of an incorrect version, but got {qe.cause_status_code} instead",
127+
query_timestamps=qe.query_timestamps,
128+
)
129+
self.end_test_step()

monitoring/uss_qualifier/scenarios/definitions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
from monitoring.uss_qualifier.resources.definitions import ResourceID
44

55
TestScenarioTypeName = str
6-
"""This plain string represents a type of test scenario, expressed as a Python class name qualified relative to the `uss_qualifier` module"""
6+
"""This plain string represents a type of test scenario, expressed as a Python class name qualified relative to the
7+
`uss_qualifier` module.
8+
9+
Note that equality between different TestScenarioTypeNames (whether they refer to the same type of test scenario) should
10+
be determined via are_scenario_types_equal as multiple TestScenarioTypeNames may resolve to the same test scenario type.
11+
"""
712

813

914
class TestScenarioDeclaration(ImplicitDict):

0 commit comments

Comments
 (0)