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.

Files changed (135) hide show
  1. cactus_test_definitions/__init__.py +39 -0
  2. cactus_test_definitions/__pycache__/__init__.cpython-312.pyc +0 -0
  3. cactus_test_definitions/__pycache__/actions.cpython-312.pyc +0 -0
  4. cactus_test_definitions/__pycache__/checks.cpython-312.pyc +0 -0
  5. cactus_test_definitions/__pycache__/csipaus.cpython-312.pyc +0 -0
  6. cactus_test_definitions/__pycache__/errors.cpython-312.pyc +0 -0
  7. cactus_test_definitions/__pycache__/events.cpython-312.pyc +0 -0
  8. cactus_test_definitions/__pycache__/parameters.cpython-312.pyc +0 -0
  9. cactus_test_definitions/__pycache__/schema.cpython-312.pyc +0 -0
  10. cactus_test_definitions/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
  11. cactus_test_definitions/__pycache__/test_procedures.cpython-312.pyc +0 -0
  12. cactus_test_definitions/__pycache__/variable_expressions.cpython-312.pyc +0 -0
  13. cactus_test_definitions/__pycache__/version.cpython-312.pyc +0 -0
  14. cactus_test_definitions/client/__init__.py +26 -0
  15. cactus_test_definitions/client/__pycache__/__init__.cpython-312.pyc +0 -0
  16. cactus_test_definitions/client/__pycache__/actions.cpython-312.pyc +0 -0
  17. cactus_test_definitions/client/__pycache__/checks.cpython-312.pyc +0 -0
  18. cactus_test_definitions/client/__pycache__/events.cpython-312.pyc +0 -0
  19. cactus_test_definitions/client/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
  20. cactus_test_definitions/client/actions.py +98 -0
  21. cactus_test_definitions/client/checks.py +117 -0
  22. cactus_test_definitions/client/events.py +71 -0
  23. cactus_test_definitions/client/procedures/ALL-01.yaml +94 -0
  24. cactus_test_definitions/client/procedures/ALL-02.yaml +108 -0
  25. cactus_test_definitions/client/procedures/ALL-03-REJ.yaml +69 -0
  26. cactus_test_definitions/client/procedures/ALL-03.yaml +110 -0
  27. cactus_test_definitions/client/procedures/ALL-04.yaml +69 -0
  28. cactus_test_definitions/client/procedures/ALL-05.yaml +117 -0
  29. cactus_test_definitions/client/procedures/ALL-06.yaml +128 -0
  30. cactus_test_definitions/client/procedures/ALL-07.yaml +76 -0
  31. cactus_test_definitions/client/procedures/ALL-08.yaml +78 -0
  32. cactus_test_definitions/client/procedures/ALL-09.yaml +103 -0
  33. cactus_test_definitions/client/procedures/ALL-10.yaml +128 -0
  34. cactus_test_definitions/client/procedures/ALL-11.yaml +111 -0
  35. cactus_test_definitions/client/procedures/ALL-12.yaml +108 -0
  36. cactus_test_definitions/client/procedures/ALL-13.yaml +112 -0
  37. cactus_test_definitions/client/procedures/ALL-14.yaml +165 -0
  38. cactus_test_definitions/client/procedures/ALL-15.yaml +109 -0
  39. cactus_test_definitions/client/procedures/ALL-16.yaml +102 -0
  40. cactus_test_definitions/client/procedures/ALL-17.yaml +63 -0
  41. cactus_test_definitions/client/procedures/ALL-18.yaml +288 -0
  42. cactus_test_definitions/client/procedures/ALL-19.yaml +78 -0
  43. cactus_test_definitions/client/procedures/ALL-20.yaml +136 -0
  44. cactus_test_definitions/client/procedures/ALL-21.yaml +203 -0
  45. cactus_test_definitions/client/procedures/ALL-22.yaml +82 -0
  46. cactus_test_definitions/client/procedures/ALL-23.yaml +158 -0
  47. cactus_test_definitions/client/procedures/ALL-24.yaml +132 -0
  48. cactus_test_definitions/client/procedures/ALL-25.yaml +136 -0
  49. cactus_test_definitions/client/procedures/ALL-26.yaml +147 -0
  50. cactus_test_definitions/client/procedures/ALL-27.yaml +144 -0
  51. cactus_test_definitions/client/procedures/ALL-28.yaml +274 -0
  52. cactus_test_definitions/client/procedures/ALL-29.yaml +87 -0
  53. cactus_test_definitions/client/procedures/ALL-30.yaml +188 -0
  54. cactus_test_definitions/client/procedures/BES-01.yaml +136 -0
  55. cactus_test_definitions/client/procedures/BES-02.yaml +137 -0
  56. cactus_test_definitions/client/procedures/BES-03.yaml +135 -0
  57. cactus_test_definitions/client/procedures/BES-04.yaml +228 -0
  58. cactus_test_definitions/client/procedures/DRA-01.yaml +54 -0
  59. cactus_test_definitions/client/procedures/DRA-02.yaml +64 -0
  60. cactus_test_definitions/client/procedures/DRD-01.yaml +667 -0
  61. cactus_test_definitions/client/procedures/DRL-01.yaml +327 -0
  62. cactus_test_definitions/client/procedures/GEN-01.yaml +73 -0
  63. cactus_test_definitions/client/procedures/GEN-02.yaml +72 -0
  64. cactus_test_definitions/client/procedures/GEN-03.yaml +160 -0
  65. cactus_test_definitions/client/procedures/GEN-04.yaml +161 -0
  66. cactus_test_definitions/client/procedures/GEN-05.yaml +89 -0
  67. cactus_test_definitions/client/procedures/GEN-06.yaml +90 -0
  68. cactus_test_definitions/client/procedures/GEN-07.yaml +145 -0
  69. cactus_test_definitions/client/procedures/GEN-08.yaml +145 -0
  70. cactus_test_definitions/client/procedures/GEN-09.yaml +117 -0
  71. cactus_test_definitions/client/procedures/GEN-10.yaml +737 -0
  72. cactus_test_definitions/client/procedures/GEN-11.yaml +376 -0
  73. cactus_test_definitions/client/procedures/GEN-12.yaml +376 -0
  74. cactus_test_definitions/client/procedures/GEN-13.yaml +70 -0
  75. cactus_test_definitions/client/procedures/LOA-01.yaml +73 -0
  76. cactus_test_definitions/client/procedures/LOA-02.yaml +73 -0
  77. cactus_test_definitions/client/procedures/LOA-03.yaml +160 -0
  78. cactus_test_definitions/client/procedures/LOA-04.yaml +161 -0
  79. cactus_test_definitions/client/procedures/LOA-05.yaml +85 -0
  80. cactus_test_definitions/client/procedures/LOA-06.yaml +85 -0
  81. cactus_test_definitions/client/procedures/LOA-07.yaml +145 -0
  82. cactus_test_definitions/client/procedures/LOA-08.yaml +145 -0
  83. cactus_test_definitions/client/procedures/LOA-09.yaml +117 -0
  84. cactus_test_definitions/client/procedures/LOA-10.yaml +739 -0
  85. cactus_test_definitions/client/procedures/LOA-11.yaml +376 -0
  86. cactus_test_definitions/client/procedures/LOA-12.yaml +376 -0
  87. cactus_test_definitions/client/procedures/LOA-13.yaml +71 -0
  88. cactus_test_definitions/client/procedures/MUL-01.yaml +92 -0
  89. cactus_test_definitions/client/procedures/MUL-02.yaml +80 -0
  90. cactus_test_definitions/client/procedures/MUL-03.yaml +78 -0
  91. cactus_test_definitions/client/procedures/OPT-1-IN-BAND.yaml +115 -0
  92. cactus_test_definitions/client/procedures/OPT-1-OUT-OF-BAND.yaml +101 -0
  93. cactus_test_definitions/client/procedures/test-procedures.yaml +75 -0
  94. cactus_test_definitions/client/test_procedures.py +296 -0
  95. cactus_test_definitions/csipaus.py +81 -0
  96. cactus_test_definitions/errors.py +15 -0
  97. cactus_test_definitions/parameters.py +149 -0
  98. cactus_test_definitions/py.typed +0 -0
  99. cactus_test_definitions/schema.py +22 -0
  100. cactus_test_definitions/server/README.md +170 -0
  101. cactus_test_definitions/server/__pycache__/actions.cpython-312.pyc +0 -0
  102. cactus_test_definitions/server/__pycache__/checks.cpython-312.pyc +0 -0
  103. cactus_test_definitions/server/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
  104. cactus_test_definitions/server/actions.py +139 -0
  105. cactus_test_definitions/server/checks.py +117 -0
  106. cactus_test_definitions/server/procedures/S-ALL-01.yaml +42 -0
  107. cactus_test_definitions/server/procedures/S-ALL-02.yaml +65 -0
  108. cactus_test_definitions/server/procedures/S-ALL-03.yaml +65 -0
  109. cactus_test_definitions/server/procedures/S-ALL-04.yaml +137 -0
  110. cactus_test_definitions/server/procedures/S-ALL-05.yaml +111 -0
  111. cactus_test_definitions/server/procedures/S-OPT-01.yaml +42 -0
  112. cactus_test_definitions/server/procedures/S-OPT-02.yaml +40 -0
  113. cactus_test_definitions/server/procedures/S-OPT-03.yaml +44 -0
  114. cactus_test_definitions/server/procedures/S-OPT-04.yaml +32 -0
  115. cactus_test_definitions/server/procedures/test-procedures.yaml +14 -0
  116. cactus_test_definitions/server/test_procedures.py +183 -0
  117. cactus_test_definitions/variable_expressions.py +419 -0
  118. cactus_test_definitions-1.0.0.dist-info/METADATA +288 -0
  119. cactus_test_definitions-1.0.0.dist-info/RECORD +135 -0
  120. cactus_test_definitions-1.0.0.dist-info/WHEEL +5 -0
  121. cactus_test_definitions-1.0.0.dist-info/licenses/LICENSE.txt +22 -0
  122. cactus_test_definitions-1.0.0.dist-info/top_level.txt +2 -0
  123. tests/__init__.py +0 -0
  124. tests/unit/__init__.py +0 -0
  125. tests/unit/client/__init__.py +0 -0
  126. tests/unit/client/test_actions.py +72 -0
  127. tests/unit/client/test_checks.py +47 -0
  128. tests/unit/client/test_config.py +61 -0
  129. tests/unit/client/test_events.py +36 -0
  130. tests/unit/client/test_test_procedures.py +150 -0
  131. tests/unit/server/__init__.py +0 -0
  132. tests/unit/server/test_test_procedures.py +86 -0
  133. tests/unit/test_csipaus.py +49 -0
  134. tests/unit/test_parameters.py +197 -0
  135. 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
+ ]
@@ -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
+ ]
@@ -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: {}