cactus-test-definitions 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cactus-test-definitions might be problematic. Click here for more details.
- cactus_test_definitions/__init__.py +39 -0
- cactus_test_definitions/__pycache__/__init__.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/actions.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/checks.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/csipaus.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/errors.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/events.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/parameters.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/schema.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
- cactus_test_definitions/__pycache__/test_procedures.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/variable_expressions.cpython-312.pyc +0 -0
- cactus_test_definitions/__pycache__/version.cpython-312.pyc +0 -0
- cactus_test_definitions/client/__init__.py +26 -0
- cactus_test_definitions/client/__pycache__/__init__.cpython-312.pyc +0 -0
- cactus_test_definitions/client/__pycache__/actions.cpython-312.pyc +0 -0
- cactus_test_definitions/client/__pycache__/checks.cpython-312.pyc +0 -0
- cactus_test_definitions/client/__pycache__/events.cpython-312.pyc +0 -0
- cactus_test_definitions/client/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
- cactus_test_definitions/client/actions.py +98 -0
- cactus_test_definitions/client/checks.py +117 -0
- cactus_test_definitions/client/events.py +71 -0
- cactus_test_definitions/client/procedures/ALL-01.yaml +94 -0
- cactus_test_definitions/client/procedures/ALL-02.yaml +108 -0
- cactus_test_definitions/client/procedures/ALL-03-REJ.yaml +69 -0
- cactus_test_definitions/client/procedures/ALL-03.yaml +110 -0
- cactus_test_definitions/client/procedures/ALL-04.yaml +69 -0
- cactus_test_definitions/client/procedures/ALL-05.yaml +117 -0
- cactus_test_definitions/client/procedures/ALL-06.yaml +128 -0
- cactus_test_definitions/client/procedures/ALL-07.yaml +76 -0
- cactus_test_definitions/client/procedures/ALL-08.yaml +78 -0
- cactus_test_definitions/client/procedures/ALL-09.yaml +103 -0
- cactus_test_definitions/client/procedures/ALL-10.yaml +128 -0
- cactus_test_definitions/client/procedures/ALL-11.yaml +111 -0
- cactus_test_definitions/client/procedures/ALL-12.yaml +108 -0
- cactus_test_definitions/client/procedures/ALL-13.yaml +112 -0
- cactus_test_definitions/client/procedures/ALL-14.yaml +165 -0
- cactus_test_definitions/client/procedures/ALL-15.yaml +109 -0
- cactus_test_definitions/client/procedures/ALL-16.yaml +102 -0
- cactus_test_definitions/client/procedures/ALL-17.yaml +63 -0
- cactus_test_definitions/client/procedures/ALL-18.yaml +288 -0
- cactus_test_definitions/client/procedures/ALL-19.yaml +78 -0
- cactus_test_definitions/client/procedures/ALL-20.yaml +136 -0
- cactus_test_definitions/client/procedures/ALL-21.yaml +203 -0
- cactus_test_definitions/client/procedures/ALL-22.yaml +82 -0
- cactus_test_definitions/client/procedures/ALL-23.yaml +158 -0
- cactus_test_definitions/client/procedures/ALL-24.yaml +132 -0
- cactus_test_definitions/client/procedures/ALL-25.yaml +136 -0
- cactus_test_definitions/client/procedures/ALL-26.yaml +147 -0
- cactus_test_definitions/client/procedures/ALL-27.yaml +144 -0
- cactus_test_definitions/client/procedures/ALL-28.yaml +274 -0
- cactus_test_definitions/client/procedures/ALL-29.yaml +87 -0
- cactus_test_definitions/client/procedures/ALL-30.yaml +188 -0
- cactus_test_definitions/client/procedures/BES-01.yaml +136 -0
- cactus_test_definitions/client/procedures/BES-02.yaml +137 -0
- cactus_test_definitions/client/procedures/BES-03.yaml +135 -0
- cactus_test_definitions/client/procedures/BES-04.yaml +228 -0
- cactus_test_definitions/client/procedures/DRA-01.yaml +54 -0
- cactus_test_definitions/client/procedures/DRA-02.yaml +64 -0
- cactus_test_definitions/client/procedures/DRD-01.yaml +667 -0
- cactus_test_definitions/client/procedures/DRL-01.yaml +327 -0
- cactus_test_definitions/client/procedures/GEN-01.yaml +73 -0
- cactus_test_definitions/client/procedures/GEN-02.yaml +72 -0
- cactus_test_definitions/client/procedures/GEN-03.yaml +160 -0
- cactus_test_definitions/client/procedures/GEN-04.yaml +161 -0
- cactus_test_definitions/client/procedures/GEN-05.yaml +89 -0
- cactus_test_definitions/client/procedures/GEN-06.yaml +90 -0
- cactus_test_definitions/client/procedures/GEN-07.yaml +145 -0
- cactus_test_definitions/client/procedures/GEN-08.yaml +145 -0
- cactus_test_definitions/client/procedures/GEN-09.yaml +117 -0
- cactus_test_definitions/client/procedures/GEN-10.yaml +737 -0
- cactus_test_definitions/client/procedures/GEN-11.yaml +376 -0
- cactus_test_definitions/client/procedures/GEN-12.yaml +376 -0
- cactus_test_definitions/client/procedures/GEN-13.yaml +70 -0
- cactus_test_definitions/client/procedures/LOA-01.yaml +73 -0
- cactus_test_definitions/client/procedures/LOA-02.yaml +73 -0
- cactus_test_definitions/client/procedures/LOA-03.yaml +160 -0
- cactus_test_definitions/client/procedures/LOA-04.yaml +161 -0
- cactus_test_definitions/client/procedures/LOA-05.yaml +85 -0
- cactus_test_definitions/client/procedures/LOA-06.yaml +85 -0
- cactus_test_definitions/client/procedures/LOA-07.yaml +145 -0
- cactus_test_definitions/client/procedures/LOA-08.yaml +145 -0
- cactus_test_definitions/client/procedures/LOA-09.yaml +117 -0
- cactus_test_definitions/client/procedures/LOA-10.yaml +739 -0
- cactus_test_definitions/client/procedures/LOA-11.yaml +376 -0
- cactus_test_definitions/client/procedures/LOA-12.yaml +376 -0
- cactus_test_definitions/client/procedures/LOA-13.yaml +71 -0
- cactus_test_definitions/client/procedures/MUL-01.yaml +92 -0
- cactus_test_definitions/client/procedures/MUL-02.yaml +80 -0
- cactus_test_definitions/client/procedures/MUL-03.yaml +78 -0
- cactus_test_definitions/client/procedures/OPT-1-IN-BAND.yaml +115 -0
- cactus_test_definitions/client/procedures/OPT-1-OUT-OF-BAND.yaml +101 -0
- cactus_test_definitions/client/procedures/test-procedures.yaml +75 -0
- cactus_test_definitions/client/test_procedures.py +296 -0
- cactus_test_definitions/csipaus.py +81 -0
- cactus_test_definitions/errors.py +15 -0
- cactus_test_definitions/parameters.py +149 -0
- cactus_test_definitions/py.typed +0 -0
- cactus_test_definitions/schema.py +22 -0
- cactus_test_definitions/server/README.md +170 -0
- cactus_test_definitions/server/__pycache__/actions.cpython-312.pyc +0 -0
- cactus_test_definitions/server/__pycache__/checks.cpython-312.pyc +0 -0
- cactus_test_definitions/server/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
- cactus_test_definitions/server/actions.py +139 -0
- cactus_test_definitions/server/checks.py +117 -0
- cactus_test_definitions/server/procedures/S-ALL-01.yaml +42 -0
- cactus_test_definitions/server/procedures/S-ALL-02.yaml +65 -0
- cactus_test_definitions/server/procedures/S-ALL-03.yaml +65 -0
- cactus_test_definitions/server/procedures/S-ALL-04.yaml +137 -0
- cactus_test_definitions/server/procedures/S-ALL-05.yaml +111 -0
- cactus_test_definitions/server/procedures/S-OPT-01.yaml +42 -0
- cactus_test_definitions/server/procedures/S-OPT-02.yaml +40 -0
- cactus_test_definitions/server/procedures/S-OPT-03.yaml +44 -0
- cactus_test_definitions/server/procedures/S-OPT-04.yaml +32 -0
- cactus_test_definitions/server/procedures/test-procedures.yaml +14 -0
- cactus_test_definitions/server/test_procedures.py +183 -0
- cactus_test_definitions/variable_expressions.py +419 -0
- cactus_test_definitions-1.0.0.dist-info/METADATA +288 -0
- cactus_test_definitions-1.0.0.dist-info/RECORD +135 -0
- cactus_test_definitions-1.0.0.dist-info/WHEEL +5 -0
- cactus_test_definitions-1.0.0.dist-info/licenses/LICENSE.txt +22 -0
- cactus_test_definitions-1.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/unit/__init__.py +0 -0
- tests/unit/client/__init__.py +0 -0
- tests/unit/client/test_actions.py +72 -0
- tests/unit/client/test_checks.py +47 -0
- tests/unit/client/test_config.py +61 -0
- tests/unit/client/test_events.py +36 -0
- tests/unit/client/test_test_procedures.py +150 -0
- tests/unit/server/__init__.py +0 -0
- tests/unit/server/test_test_procedures.py +86 -0
- tests/unit/test_csipaus.py +49 -0
- tests/unit/test_parameters.py +197 -0
- tests/unit/test_variable_expressions.py +402 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from cactus_test_definitions.csipaus import CSIPAusVersion
|
|
2
|
+
from cactus_test_definitions.errors import (
|
|
3
|
+
TestProcedureDefinitionError,
|
|
4
|
+
UnparseableVariableExpressionError,
|
|
5
|
+
UnresolvableVariableError,
|
|
6
|
+
)
|
|
7
|
+
from cactus_test_definitions.variable_expressions import (
|
|
8
|
+
Constant,
|
|
9
|
+
ConstantType,
|
|
10
|
+
Expression,
|
|
11
|
+
NamedVariable,
|
|
12
|
+
NamedVariableType,
|
|
13
|
+
OperationType,
|
|
14
|
+
parse_binary_expression,
|
|
15
|
+
parse_time_delta,
|
|
16
|
+
parse_unary_expression,
|
|
17
|
+
parse_variable_expression_body,
|
|
18
|
+
try_extract_variable_expression,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__version__ = "1.0.0"
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"TestProcedureDefinitionError",
|
|
25
|
+
"CSIPAusVersion",
|
|
26
|
+
"Expression",
|
|
27
|
+
"Constant",
|
|
28
|
+
"ConstantType",
|
|
29
|
+
"NamedVariable",
|
|
30
|
+
"OperationType",
|
|
31
|
+
"UnresolvableVariableError",
|
|
32
|
+
"UnparseableVariableExpressionError",
|
|
33
|
+
"NamedVariableType",
|
|
34
|
+
"try_extract_variable_expression",
|
|
35
|
+
"parse_variable_expression_body",
|
|
36
|
+
"parse_time_delta",
|
|
37
|
+
"parse_binary_expression",
|
|
38
|
+
"parse_unary_expression",
|
|
39
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from cactus_test_definitions.client.actions import ACTION_PARAMETER_SCHEMA, Action
|
|
2
|
+
from cactus_test_definitions.client.checks import CHECK_PARAMETER_SCHEMA, Check
|
|
3
|
+
from cactus_test_definitions.client.events import EVENT_PARAMETER_SCHEMA, Event
|
|
4
|
+
from cactus_test_definitions.client.test_procedures import (
|
|
5
|
+
Preconditions,
|
|
6
|
+
Step,
|
|
7
|
+
TestProcedure,
|
|
8
|
+
TestProcedureConfig,
|
|
9
|
+
TestProcedureId,
|
|
10
|
+
TestProcedures,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"TestProcedureId",
|
|
15
|
+
"Event",
|
|
16
|
+
"Action",
|
|
17
|
+
"ACTION_PARAMETER_SCHEMA",
|
|
18
|
+
"Check",
|
|
19
|
+
"CHECK_PARAMETER_SCHEMA",
|
|
20
|
+
"EVENT_PARAMETER_SCHEMA",
|
|
21
|
+
"Step",
|
|
22
|
+
"Preconditions",
|
|
23
|
+
"TestProcedure",
|
|
24
|
+
"TestProcedures",
|
|
25
|
+
"TestProcedureConfig",
|
|
26
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cactus_test_definitions.errors import TestProcedureDefinitionError
|
|
5
|
+
from cactus_test_definitions.parameters import (
|
|
6
|
+
ParameterSchema,
|
|
7
|
+
ParameterType,
|
|
8
|
+
validate_parameters,
|
|
9
|
+
)
|
|
10
|
+
from cactus_test_definitions.variable_expressions import (
|
|
11
|
+
parse_variable_expression_body,
|
|
12
|
+
try_extract_variable_expression,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Action:
|
|
18
|
+
type: str
|
|
19
|
+
parameters: dict[str, Any]
|
|
20
|
+
|
|
21
|
+
def __post_init__(self):
|
|
22
|
+
"""Some parameter values might contain variable expressions (eg: a string "$now") that needs to be replaced
|
|
23
|
+
with an parsed Expression object instead."""
|
|
24
|
+
for k, v in self.parameters.items():
|
|
25
|
+
variable_expr = try_extract_variable_expression(v)
|
|
26
|
+
if variable_expr:
|
|
27
|
+
self.parameters[k] = parse_variable_expression_body(variable_expr, k)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# The parameter schema for each action, keyed by the action name
|
|
31
|
+
ACTION_PARAMETER_SCHEMA: dict[str, dict[str, ParameterSchema]] = {
|
|
32
|
+
"enable-steps": {"steps": ParameterSchema(True, ParameterType.ListString)},
|
|
33
|
+
"remove-steps": {"steps": ParameterSchema(True, ParameterType.ListString)},
|
|
34
|
+
"finish-test": {},
|
|
35
|
+
"set-default-der-control": {
|
|
36
|
+
"derp_id": ParameterSchema(False, ParameterType.Integer),
|
|
37
|
+
"opModImpLimW": ParameterSchema(False, ParameterType.Float),
|
|
38
|
+
"opModExpLimW": ParameterSchema(False, ParameterType.Float),
|
|
39
|
+
"opModGenLimW": ParameterSchema(False, ParameterType.Float),
|
|
40
|
+
"opModLoadLimW": ParameterSchema(False, ParameterType.Float),
|
|
41
|
+
"opModStorageTargetW": ParameterSchema(False, ParameterType.Float),
|
|
42
|
+
"setGradW": ParameterSchema(False, ParameterType.Integer), # Hundredths of a percent / second
|
|
43
|
+
"cancelled": ParameterSchema(False, ParameterType.Boolean),
|
|
44
|
+
},
|
|
45
|
+
"create-der-control": {
|
|
46
|
+
"start": ParameterSchema(True, ParameterType.DateTime),
|
|
47
|
+
"duration_seconds": ParameterSchema(True, ParameterType.Integer),
|
|
48
|
+
"pow_10_multipliers": ParameterSchema(False, ParameterType.Integer),
|
|
49
|
+
"primacy": ParameterSchema(False, ParameterType.Integer),
|
|
50
|
+
"fsa_id": ParameterSchema(False, ParameterType.Integer),
|
|
51
|
+
"randomizeStart_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
52
|
+
"ramp_time_seconds": ParameterSchema(False, ParameterType.Float),
|
|
53
|
+
"opModEnergize": ParameterSchema(False, ParameterType.Boolean),
|
|
54
|
+
"opModConnect": ParameterSchema(False, ParameterType.Boolean),
|
|
55
|
+
"opModImpLimW": ParameterSchema(False, ParameterType.Float),
|
|
56
|
+
"opModExpLimW": ParameterSchema(False, ParameterType.Float),
|
|
57
|
+
"opModGenLimW": ParameterSchema(False, ParameterType.Float),
|
|
58
|
+
"opModLoadLimW": ParameterSchema(False, ParameterType.Float),
|
|
59
|
+
"opModFixedW": ParameterSchema(False, ParameterType.Float),
|
|
60
|
+
"opModStorageTargetW": ParameterSchema(False, ParameterType.Float),
|
|
61
|
+
},
|
|
62
|
+
"create-der-program": {
|
|
63
|
+
"primacy": ParameterSchema(True, ParameterType.Integer),
|
|
64
|
+
"fsa_id": ParameterSchema(False, ParameterType.Integer),
|
|
65
|
+
},
|
|
66
|
+
"cancel-active-der-controls": {},
|
|
67
|
+
"set-comms-rate": {
|
|
68
|
+
"dcap_poll_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
69
|
+
"edev_post_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
70
|
+
"edev_list_poll_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
71
|
+
"fsa_list_poll_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
72
|
+
"derp_list_poll_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
73
|
+
"der_list_poll_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
74
|
+
"mup_post_seconds": ParameterSchema(False, ParameterType.Integer),
|
|
75
|
+
},
|
|
76
|
+
"communications-status": {"enabled": ParameterSchema(True, ParameterType.Boolean)},
|
|
77
|
+
"edev-registration-links": {"enabled": ParameterSchema(True, ParameterType.Boolean)},
|
|
78
|
+
"register-end-device": {
|
|
79
|
+
"nmi": ParameterSchema(False, ParameterType.String),
|
|
80
|
+
"registration_pin": ParameterSchema(False, ParameterType.Integer),
|
|
81
|
+
"aggregator_lfdi": ParameterSchema(False, ParameterType.HexBinary),
|
|
82
|
+
"aggregator_sfdi": ParameterSchema(False, ParameterType.Integer),
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
VALID_ACTION_NAMES: set[str] = set(ACTION_PARAMETER_SCHEMA.keys())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def validate_action_parameters(procedure_name: str, step: str, action: Action) -> None:
|
|
89
|
+
"""Validates the action parameters for the parent TestProcedure based on the ACTION_PARAMETER_SCHEMA
|
|
90
|
+
|
|
91
|
+
raises TestProcedureDefinitionError on failure"""
|
|
92
|
+
location = f"{procedure_name}.{step} Action: {action.type}" # Descriptive location of this action being validated
|
|
93
|
+
|
|
94
|
+
parameter_schema = ACTION_PARAMETER_SCHEMA.get(action.type, None)
|
|
95
|
+
if parameter_schema is None:
|
|
96
|
+
raise TestProcedureDefinitionError(f"{location} not a valid action name. {VALID_ACTION_NAMES}")
|
|
97
|
+
|
|
98
|
+
validate_parameters(location, action.parameters, parameter_schema)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cactus_test_definitions.errors import TestProcedureDefinitionError
|
|
5
|
+
from cactus_test_definitions.parameters import (
|
|
6
|
+
ParameterSchema,
|
|
7
|
+
ParameterType,
|
|
8
|
+
validate_parameters,
|
|
9
|
+
)
|
|
10
|
+
from cactus_test_definitions.variable_expressions import (
|
|
11
|
+
parse_variable_expression_body,
|
|
12
|
+
try_extract_variable_expression,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Check:
|
|
18
|
+
"""A check represents some validation logic that runs during test finalization and can provide a pass/fail
|
|
19
|
+
status beyond the "basic" flow of a test procedure. Checks will typically inspect the database/history of requests
|
|
20
|
+
ino order to determine compliance.
|
|
21
|
+
|
|
22
|
+
eg: Looking through past requests to an endpoint to validate it was called at a specific rate"""
|
|
23
|
+
|
|
24
|
+
type: str
|
|
25
|
+
parameters: dict[str, Any]
|
|
26
|
+
|
|
27
|
+
def __post_init__(self):
|
|
28
|
+
"""Some parameter values might contain variable expressions (eg: a string "$now") that needs to be replaced
|
|
29
|
+
with an parsed Expression object instead."""
|
|
30
|
+
for k, v in self.parameters.items():
|
|
31
|
+
variable_expr = try_extract_variable_expression(v)
|
|
32
|
+
if variable_expr:
|
|
33
|
+
self.parameters[k] = parse_variable_expression_body(variable_expr, k)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# The parameter schema for each action, keyed by the action name
|
|
37
|
+
CHECK_PARAMETER_SCHEMA: dict[str, dict[str, ParameterSchema]] = {
|
|
38
|
+
"all-steps-complete": {"ignored_steps": ParameterSchema(False, ParameterType.ListString)},
|
|
39
|
+
"all-notifications-transmitted": {},
|
|
40
|
+
"end-device-contents": {
|
|
41
|
+
"has_connection_point_id": ParameterSchema(False, ParameterType.Boolean),
|
|
42
|
+
"deviceCategory_anyset": ParameterSchema(False, ParameterType.HexBinary), # Any of these bits set to 1
|
|
43
|
+
"check_lfdi": ParameterSchema(False, ParameterType.Boolean), # Should LFDI be validated in detail
|
|
44
|
+
},
|
|
45
|
+
"der-settings-contents": {
|
|
46
|
+
"setGradW": ParameterSchema(False, ParameterType.Integer), # Hundredths of a percent / second
|
|
47
|
+
"doeModesEnabled": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
48
|
+
"doeModesEnabled_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
49
|
+
"doeModesEnabled_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
50
|
+
"modesEnabled_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
51
|
+
"modesEnabled_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
52
|
+
"vppModesEnabled_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
53
|
+
"vppModesEnabled_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
54
|
+
"setMaxVA": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
55
|
+
"setMaxVar": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
56
|
+
"setMaxVarNeg": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
57
|
+
"setMaxW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
58
|
+
"setMaxChargeRateW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
59
|
+
"setMaxDischargeRateW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
60
|
+
"setMaxWh": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
61
|
+
"setMinWh": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
62
|
+
"setMinPFOverExcited": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
63
|
+
"setMinPFUnderExcited": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
64
|
+
},
|
|
65
|
+
"der-capability-contents": {
|
|
66
|
+
"doeModesSupported": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
67
|
+
"doeModesSupported_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
68
|
+
"doeModesSupported_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
69
|
+
"modesSupported_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
70
|
+
"modesSupported_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
71
|
+
"vppModesSupported_set": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to one
|
|
72
|
+
"vppModesSupported_unset": ParameterSchema(False, ParameterType.HexBinary), # Minimum bits set to zero
|
|
73
|
+
"rtgMaxVA": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
74
|
+
"rtgMaxVar": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
75
|
+
"rtgMaxVarNeg": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
76
|
+
"rtgMaxW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
77
|
+
"rtgMaxChargeRateW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
78
|
+
"rtgMaxDischargeRateW": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
79
|
+
"rtgMaxWh": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
80
|
+
"rtgMinPFOverExcited": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
81
|
+
"rtgMinPFUnderExcited": ParameterSchema(False, ParameterType.Boolean), # Is ANY value set?
|
|
82
|
+
},
|
|
83
|
+
"der-status-contents": {
|
|
84
|
+
"genConnectStatus": ParameterSchema(False, ParameterType.Integer),
|
|
85
|
+
"genConnectStatus_bit0": ParameterSchema(False, ParameterType.Boolean),
|
|
86
|
+
"genConnectStatus_bit1": ParameterSchema(False, ParameterType.Boolean),
|
|
87
|
+
"genConnectStatus_bit2": ParameterSchema(False, ParameterType.Boolean),
|
|
88
|
+
"operationalModeStatus": ParameterSchema(False, ParameterType.Integer),
|
|
89
|
+
"alarmStatus": ParameterSchema(False, ParameterType.Integer),
|
|
90
|
+
},
|
|
91
|
+
"readings-voltage": {"minimum_count": ParameterSchema(True, ParameterType.Integer)},
|
|
92
|
+
"readings-site-active-power": {"minimum_count": ParameterSchema(True, ParameterType.Integer)},
|
|
93
|
+
"readings-site-reactive-power": {"minimum_count": ParameterSchema(True, ParameterType.Integer)},
|
|
94
|
+
"readings-der-active-power": {"minimum_count": ParameterSchema(True, ParameterType.Integer)},
|
|
95
|
+
"readings-der-reactive-power": {"minimum_count": ParameterSchema(True, ParameterType.Integer)},
|
|
96
|
+
"readings-der-stored-energy": {"minimum_count": ParameterSchema(True, ParameterType.Integer)}, # Storage extension
|
|
97
|
+
"subscription-contents": {"subscribed_resource": ParameterSchema(True, ParameterType.String)},
|
|
98
|
+
"response-contents": {
|
|
99
|
+
"latest": ParameterSchema(False, ParameterType.Boolean),
|
|
100
|
+
"status": ParameterSchema(False, ParameterType.Integer),
|
|
101
|
+
"all": ParameterSchema(False, ParameterType.Boolean),
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
VALID_CHECK_NAMES: set[str] = set(CHECK_PARAMETER_SCHEMA.keys())
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def validate_check_parameters(procedure_name: str, check: Check) -> None:
|
|
108
|
+
"""Validates the check parameters for the parent TestProcedure based on the CHECK_PARAMETER_SCHEMA
|
|
109
|
+
|
|
110
|
+
raises TestProcedureDefinitionError on failure"""
|
|
111
|
+
location = f"{procedure_name} Check: {check.type}" # Descriptive location of this action being validated
|
|
112
|
+
|
|
113
|
+
parameter_schema = CHECK_PARAMETER_SCHEMA.get(check.type, None)
|
|
114
|
+
if parameter_schema is None:
|
|
115
|
+
raise TestProcedureDefinitionError(f"{location} not a valid action name. {VALID_CHECK_NAMES}")
|
|
116
|
+
|
|
117
|
+
validate_parameters(location, check.parameters, parameter_schema)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cactus_test_definitions.client.checks import Check
|
|
5
|
+
from cactus_test_definitions.errors import TestProcedureDefinitionError
|
|
6
|
+
from cactus_test_definitions.parameters import (
|
|
7
|
+
ParameterSchema,
|
|
8
|
+
ParameterType,
|
|
9
|
+
validate_parameters,
|
|
10
|
+
)
|
|
11
|
+
from cactus_test_definitions.variable_expressions import (
|
|
12
|
+
parse_variable_expression_body,
|
|
13
|
+
try_extract_variable_expression,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Event:
|
|
19
|
+
"""An event represents some form of client/other criteria occurring (trigger). When an event trigger is met,
|
|
20
|
+
any associated actions with the parent Step will be run.
|
|
21
|
+
|
|
22
|
+
Events can have checks that must be all returned True/Pass at the moment the event is triggered otherwise
|
|
23
|
+
the event trigger will be ignored."""
|
|
24
|
+
|
|
25
|
+
type: str # The type of event being listened for
|
|
26
|
+
parameters: dict[str, Any] # Any parameters to the event listener
|
|
27
|
+
checks: list[Check] | None = None # This event will be blocked from triggering if any of these checks return False
|
|
28
|
+
|
|
29
|
+
def __post_init__(self):
|
|
30
|
+
"""Some parameter values might contain variable expressions (eg: a string "$now") that needs to be replaced
|
|
31
|
+
with an parsed Expression object instead."""
|
|
32
|
+
for k, v in self.parameters.items():
|
|
33
|
+
variable_expr = try_extract_variable_expression(v)
|
|
34
|
+
if variable_expr:
|
|
35
|
+
self.parameters[k] = parse_variable_expression_body(variable_expr, k)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# The parameter schema for each event, keyed by the event name
|
|
39
|
+
EVENT_PARAMETER_SCHEMA: dict[str, dict[str, ParameterSchema]] = {
|
|
40
|
+
"GET-request-received": {
|
|
41
|
+
"endpoint": ParameterSchema(True, ParameterType.String),
|
|
42
|
+
"serve_request_first": ParameterSchema(False, ParameterType.Boolean),
|
|
43
|
+
},
|
|
44
|
+
"POST-request-received": {
|
|
45
|
+
"endpoint": ParameterSchema(True, ParameterType.String),
|
|
46
|
+
"serve_request_first": ParameterSchema(False, ParameterType.Boolean),
|
|
47
|
+
},
|
|
48
|
+
"PUT-request-received": {
|
|
49
|
+
"endpoint": ParameterSchema(True, ParameterType.String),
|
|
50
|
+
"serve_request_first": ParameterSchema(False, ParameterType.Boolean),
|
|
51
|
+
},
|
|
52
|
+
"DELETE-request-received": {
|
|
53
|
+
"endpoint": ParameterSchema(True, ParameterType.String),
|
|
54
|
+
"serve_request_first": ParameterSchema(False, ParameterType.Boolean),
|
|
55
|
+
},
|
|
56
|
+
"wait": {"duration_seconds": ParameterSchema(True, ParameterType.Integer)},
|
|
57
|
+
}
|
|
58
|
+
VALID_EVENT_NAMES: set[str] = set(EVENT_PARAMETER_SCHEMA.keys())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def validate_event_parameters(procedure_name: str, step: str, event: Event) -> None:
|
|
62
|
+
"""Validates the event parameters for the parent TestProcedure based on the EVENT_PARAMETER_SCHEMA
|
|
63
|
+
|
|
64
|
+
raises TestProcedureDefinitionError on failure"""
|
|
65
|
+
location = f"{procedure_name}.{step} Event: {event.type}" # Descriptive location of this event being validated
|
|
66
|
+
|
|
67
|
+
parameter_schema = EVENT_PARAMETER_SCHEMA.get(event.type, None)
|
|
68
|
+
if parameter_schema is None:
|
|
69
|
+
raise TestProcedureDefinitionError(f"{location} not a valid action name. {VALID_EVENT_NAMES}")
|
|
70
|
+
|
|
71
|
+
validate_parameters(location, event.parameters, parameter_schema)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Description: Discovery with Out-of-Band Registration
|
|
2
|
+
Category: Registration
|
|
3
|
+
Classes:
|
|
4
|
+
- A
|
|
5
|
+
- DR-A
|
|
6
|
+
|
|
7
|
+
TargetVersions:
|
|
8
|
+
- v1.2
|
|
9
|
+
- v1.3-beta/storage
|
|
10
|
+
|
|
11
|
+
Preconditions:
|
|
12
|
+
immediate_start: true # There will be no "init" phase - all interactions will be immediately logged against the test
|
|
13
|
+
init_actions:
|
|
14
|
+
- type: set-comms-rate
|
|
15
|
+
parameters:
|
|
16
|
+
dcap_poll_seconds: 60
|
|
17
|
+
edev_list_poll_seconds: 60
|
|
18
|
+
fsa_list_poll_seconds: 60
|
|
19
|
+
der_list_poll_seconds: 60
|
|
20
|
+
derp_list_poll_seconds: 60
|
|
21
|
+
mup_post_seconds: 60
|
|
22
|
+
|
|
23
|
+
actions:
|
|
24
|
+
- type: register-end-device
|
|
25
|
+
parameters:
|
|
26
|
+
registration_pin: 11111 # With checksum is 111115
|
|
27
|
+
aggregator_lfdi: 3E4F45AB31EDFE5B67E343E5E4562E3100000000 # Trailing PEN digits set to 0
|
|
28
|
+
aggregator_sfdi: 16726121139
|
|
29
|
+
|
|
30
|
+
Criteria:
|
|
31
|
+
checks:
|
|
32
|
+
- type: all-steps-complete
|
|
33
|
+
parameters: {}
|
|
34
|
+
|
|
35
|
+
Steps:
|
|
36
|
+
# (b, c, d)
|
|
37
|
+
GET-DCAP:
|
|
38
|
+
instructions:
|
|
39
|
+
- "An EndDevice has already been registered (out of band) with PIN 111115"
|
|
40
|
+
- "If you are connecting using an Aggregator client/certificate the out of band registered EndDevice will have the following:"
|
|
41
|
+
- "LFDI: 3E4F45AB31EDFE5B67E343E5E4562E31XXXXXXXX (The X values will match your client PEN)"
|
|
42
|
+
- "SFDI: 16726121139"
|
|
43
|
+
event:
|
|
44
|
+
type: GET-request-received
|
|
45
|
+
parameters:
|
|
46
|
+
endpoint: /dcap
|
|
47
|
+
actions:
|
|
48
|
+
- type: enable-steps
|
|
49
|
+
parameters:
|
|
50
|
+
steps:
|
|
51
|
+
- GET-EDEV-LIST
|
|
52
|
+
- GET-TM
|
|
53
|
+
- GET-DER
|
|
54
|
+
- type: remove-steps
|
|
55
|
+
parameters:
|
|
56
|
+
steps:
|
|
57
|
+
- GET-DCAP
|
|
58
|
+
|
|
59
|
+
# (e, f, g)
|
|
60
|
+
GET-EDEV-LIST:
|
|
61
|
+
event:
|
|
62
|
+
type: GET-request-received
|
|
63
|
+
parameters:
|
|
64
|
+
endpoint: /edev
|
|
65
|
+
actions:
|
|
66
|
+
- type: remove-steps
|
|
67
|
+
parameters:
|
|
68
|
+
steps:
|
|
69
|
+
- GET-EDEV-LIST
|
|
70
|
+
|
|
71
|
+
# (h, i, j)
|
|
72
|
+
GET-TM:
|
|
73
|
+
event:
|
|
74
|
+
type: GET-request-received
|
|
75
|
+
parameters:
|
|
76
|
+
endpoint: /tm
|
|
77
|
+
actions:
|
|
78
|
+
- type: remove-steps
|
|
79
|
+
parameters:
|
|
80
|
+
steps:
|
|
81
|
+
- GET-TM
|
|
82
|
+
|
|
83
|
+
# (k, l, m)
|
|
84
|
+
GET-DER:
|
|
85
|
+
event:
|
|
86
|
+
type: GET-request-received
|
|
87
|
+
parameters:
|
|
88
|
+
endpoint: /edev/1/der
|
|
89
|
+
actions:
|
|
90
|
+
- type: remove-steps
|
|
91
|
+
parameters:
|
|
92
|
+
steps:
|
|
93
|
+
- GET-DER
|
|
94
|
+
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Description: Discovery with In-Band Registration
|
|
2
|
+
Category: Registration
|
|
3
|
+
Classes:
|
|
4
|
+
- A
|
|
5
|
+
|
|
6
|
+
TargetVersions:
|
|
7
|
+
- v1.2
|
|
8
|
+
- v1.3-beta/storage
|
|
9
|
+
|
|
10
|
+
Preconditions:
|
|
11
|
+
immediate_start: true # There will be no "init" phase - all interactions will be immediately logged against the test
|
|
12
|
+
init_actions:
|
|
13
|
+
- type: set-comms-rate
|
|
14
|
+
parameters:
|
|
15
|
+
dcap_poll_seconds: 60
|
|
16
|
+
edev_list_poll_seconds: 60
|
|
17
|
+
fsa_list_poll_seconds: 60
|
|
18
|
+
der_list_poll_seconds: 60
|
|
19
|
+
derp_list_poll_seconds: 60
|
|
20
|
+
mup_post_seconds: 60
|
|
21
|
+
|
|
22
|
+
Criteria:
|
|
23
|
+
checks:
|
|
24
|
+
- type: all-steps-complete
|
|
25
|
+
parameters: {}
|
|
26
|
+
- type: der-capability-contents
|
|
27
|
+
parameters: {}
|
|
28
|
+
- type: der-settings-contents
|
|
29
|
+
parameters: {}
|
|
30
|
+
- type: der-status-contents
|
|
31
|
+
parameters: {}
|
|
32
|
+
- type: end-device-contents
|
|
33
|
+
parameters:
|
|
34
|
+
check_lfdi: true
|
|
35
|
+
|
|
36
|
+
Steps:
|
|
37
|
+
# (b, c, d, e, f)
|
|
38
|
+
GET-DCAP:
|
|
39
|
+
event:
|
|
40
|
+
type: GET-request-received
|
|
41
|
+
parameters:
|
|
42
|
+
endpoint: /dcap
|
|
43
|
+
actions:
|
|
44
|
+
- type: enable-steps
|
|
45
|
+
parameters:
|
|
46
|
+
steps:
|
|
47
|
+
- GET-EDEV
|
|
48
|
+
- GET-TM
|
|
49
|
+
- type: remove-steps
|
|
50
|
+
parameters:
|
|
51
|
+
steps:
|
|
52
|
+
- GET-DCAP
|
|
53
|
+
|
|
54
|
+
# (g, h, i)
|
|
55
|
+
GET-EDEV:
|
|
56
|
+
event:
|
|
57
|
+
type: GET-request-received
|
|
58
|
+
parameters:
|
|
59
|
+
endpoint: /edev
|
|
60
|
+
actions:
|
|
61
|
+
- type: enable-steps
|
|
62
|
+
parameters:
|
|
63
|
+
steps:
|
|
64
|
+
- POST-EDEV
|
|
65
|
+
- type: remove-steps
|
|
66
|
+
parameters:
|
|
67
|
+
steps:
|
|
68
|
+
- GET-EDEV
|
|
69
|
+
# (m, n, o)
|
|
70
|
+
GET-TM:
|
|
71
|
+
event:
|
|
72
|
+
type: GET-request-received
|
|
73
|
+
parameters:
|
|
74
|
+
endpoint: /tm
|
|
75
|
+
actions:
|
|
76
|
+
- type: remove-steps
|
|
77
|
+
parameters:
|
|
78
|
+
steps:
|
|
79
|
+
- GET-TM
|
|
80
|
+
|
|
81
|
+
# (j, k)
|
|
82
|
+
POST-EDEV:
|
|
83
|
+
event:
|
|
84
|
+
type: POST-request-received
|
|
85
|
+
parameters:
|
|
86
|
+
endpoint: /edev
|
|
87
|
+
actions:
|
|
88
|
+
- type: enable-steps
|
|
89
|
+
parameters:
|
|
90
|
+
steps:
|
|
91
|
+
- GET-DER
|
|
92
|
+
- type: remove-steps
|
|
93
|
+
parameters:
|
|
94
|
+
steps:
|
|
95
|
+
- POST-EDEV
|
|
96
|
+
|
|
97
|
+
# (l) - This step is implied - They should have discovered this URI EITHER through the edev list or the Location header (from POST)
|
|
98
|
+
# (p, q, r)
|
|
99
|
+
GET-DER:
|
|
100
|
+
event:
|
|
101
|
+
type: GET-request-received
|
|
102
|
+
parameters:
|
|
103
|
+
endpoint: /edev/1/der
|
|
104
|
+
actions:
|
|
105
|
+
- type: remove-steps
|
|
106
|
+
parameters:
|
|
107
|
+
steps:
|
|
108
|
+
- GET-DER
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Description: Client rejection of incorrect PIN
|
|
2
|
+
Category: Registration
|
|
3
|
+
Classes:
|
|
4
|
+
- A
|
|
5
|
+
|
|
6
|
+
TargetVersions:
|
|
7
|
+
- v1.2
|
|
8
|
+
- v1.3-beta/storage
|
|
9
|
+
|
|
10
|
+
Criteria:
|
|
11
|
+
checks:
|
|
12
|
+
- type: all-steps-complete
|
|
13
|
+
parameters: {}
|
|
14
|
+
|
|
15
|
+
Preconditions:
|
|
16
|
+
immediate_start: true # There will be no "init" phase - all interactions will be immediately logged against the test
|
|
17
|
+
init_actions:
|
|
18
|
+
- type: set-comms-rate
|
|
19
|
+
parameters:
|
|
20
|
+
dcap_poll_seconds: 60
|
|
21
|
+
edev_list_poll_seconds: 60
|
|
22
|
+
fsa_list_poll_seconds: 60
|
|
23
|
+
der_list_poll_seconds: 60
|
|
24
|
+
derp_list_poll_seconds: 60
|
|
25
|
+
mup_post_seconds: 60
|
|
26
|
+
|
|
27
|
+
actions:
|
|
28
|
+
- type: register-end-device
|
|
29
|
+
parameters:
|
|
30
|
+
registration_pin: 33221 # Different from the PIN registered in ALL-01 - Should not match client PIN
|
|
31
|
+
aggregator_lfdi: 3E4F45AB31EDFE5B67E343E5E4562E3100000000 # Trailing PEN digits set to 0
|
|
32
|
+
aggregator_sfdi: 16726121139
|
|
33
|
+
|
|
34
|
+
Steps:
|
|
35
|
+
# (a, b, c)
|
|
36
|
+
GET-REGISTRATION:
|
|
37
|
+
instructions:
|
|
38
|
+
- "An EndDevice has already been registered (out of band) with a different PIN from ALL-01"
|
|
39
|
+
- "[Aggregator Client Only] LFDI: 3E4F45AB31EDFE5B67E343E5E4562E31XXXXXXXX (The X values will match your client PEN)"
|
|
40
|
+
- "[Aggregator Client Only] SFDI: 16726121139"
|
|
41
|
+
event:
|
|
42
|
+
type: GET-request-received
|
|
43
|
+
parameters:
|
|
44
|
+
endpoint: /edev/1/rg
|
|
45
|
+
actions:
|
|
46
|
+
- type: enable-steps
|
|
47
|
+
parameters:
|
|
48
|
+
steps:
|
|
49
|
+
- WAIT-TEST-END
|
|
50
|
+
- type: remove-steps
|
|
51
|
+
parameters:
|
|
52
|
+
steps:
|
|
53
|
+
- GET-REGISTRATION
|
|
54
|
+
|
|
55
|
+
# (d)
|
|
56
|
+
WAIT-TEST-END:
|
|
57
|
+
instructions:
|
|
58
|
+
- The client should now be ceasing communications with the utility server due to an incorrect PIN being received.
|
|
59
|
+
event:
|
|
60
|
+
type: wait
|
|
61
|
+
parameters:
|
|
62
|
+
duration_seconds: 120
|
|
63
|
+
actions:
|
|
64
|
+
- type: remove-steps
|
|
65
|
+
parameters:
|
|
66
|
+
steps:
|
|
67
|
+
- WAIT-TEST-END
|
|
68
|
+
- type: finish-test
|
|
69
|
+
parameters: {}
|