cactus-test-definitions 1.6.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 (139) 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/__pycache__/validate.cpython-312.pyc +0 -0
  21. cactus_test_definitions/client/actions.py +98 -0
  22. cactus_test_definitions/client/checks.py +127 -0
  23. cactus_test_definitions/client/events.py +71 -0
  24. cactus_test_definitions/client/procedures/ALL-01.yaml +94 -0
  25. cactus_test_definitions/client/procedures/ALL-02.yaml +113 -0
  26. cactus_test_definitions/client/procedures/ALL-03-REJ.yaml +69 -0
  27. cactus_test_definitions/client/procedures/ALL-03.yaml +110 -0
  28. cactus_test_definitions/client/procedures/ALL-04.yaml +69 -0
  29. cactus_test_definitions/client/procedures/ALL-05.yaml +117 -0
  30. cactus_test_definitions/client/procedures/ALL-06.yaml +128 -0
  31. cactus_test_definitions/client/procedures/ALL-07.yaml +76 -0
  32. cactus_test_definitions/client/procedures/ALL-08.yaml +78 -0
  33. cactus_test_definitions/client/procedures/ALL-09.yaml +111 -0
  34. cactus_test_definitions/client/procedures/ALL-10.yaml +128 -0
  35. cactus_test_definitions/client/procedures/ALL-11.yaml +111 -0
  36. cactus_test_definitions/client/procedures/ALL-12.yaml +108 -0
  37. cactus_test_definitions/client/procedures/ALL-13.yaml +112 -0
  38. cactus_test_definitions/client/procedures/ALL-14.yaml +165 -0
  39. cactus_test_definitions/client/procedures/ALL-15.yaml +109 -0
  40. cactus_test_definitions/client/procedures/ALL-16.yaml +102 -0
  41. cactus_test_definitions/client/procedures/ALL-17.yaml +63 -0
  42. cactus_test_definitions/client/procedures/ALL-18.yaml +288 -0
  43. cactus_test_definitions/client/procedures/ALL-19.yaml +78 -0
  44. cactus_test_definitions/client/procedures/ALL-20.yaml +136 -0
  45. cactus_test_definitions/client/procedures/ALL-21.yaml +203 -0
  46. cactus_test_definitions/client/procedures/ALL-22.yaml +82 -0
  47. cactus_test_definitions/client/procedures/ALL-23.yaml +158 -0
  48. cactus_test_definitions/client/procedures/ALL-24.yaml +132 -0
  49. cactus_test_definitions/client/procedures/ALL-25-EXT.yaml +228 -0
  50. cactus_test_definitions/client/procedures/ALL-25.yaml +136 -0
  51. cactus_test_definitions/client/procedures/ALL-26.yaml +147 -0
  52. cactus_test_definitions/client/procedures/ALL-27.yaml +144 -0
  53. cactus_test_definitions/client/procedures/ALL-28.yaml +274 -0
  54. cactus_test_definitions/client/procedures/ALL-29.yaml +87 -0
  55. cactus_test_definitions/client/procedures/ALL-30.yaml +188 -0
  56. cactus_test_definitions/client/procedures/BES-01.yaml +136 -0
  57. cactus_test_definitions/client/procedures/BES-02.yaml +137 -0
  58. cactus_test_definitions/client/procedures/BES-03.yaml +135 -0
  59. cactus_test_definitions/client/procedures/BES-04.yaml +228 -0
  60. cactus_test_definitions/client/procedures/DRA-01.yaml +54 -0
  61. cactus_test_definitions/client/procedures/DRA-02.yaml +64 -0
  62. cactus_test_definitions/client/procedures/DRD-01.yaml +667 -0
  63. cactus_test_definitions/client/procedures/DRL-01.yaml +327 -0
  64. cactus_test_definitions/client/procedures/GEN-01.yaml +73 -0
  65. cactus_test_definitions/client/procedures/GEN-02.yaml +72 -0
  66. cactus_test_definitions/client/procedures/GEN-03.yaml +160 -0
  67. cactus_test_definitions/client/procedures/GEN-04.yaml +161 -0
  68. cactus_test_definitions/client/procedures/GEN-05.yaml +89 -0
  69. cactus_test_definitions/client/procedures/GEN-06.yaml +90 -0
  70. cactus_test_definitions/client/procedures/GEN-07.yaml +145 -0
  71. cactus_test_definitions/client/procedures/GEN-08.yaml +145 -0
  72. cactus_test_definitions/client/procedures/GEN-09.yaml +117 -0
  73. cactus_test_definitions/client/procedures/GEN-10.yaml +739 -0
  74. cactus_test_definitions/client/procedures/GEN-11.yaml +378 -0
  75. cactus_test_definitions/client/procedures/GEN-12.yaml +378 -0
  76. cactus_test_definitions/client/procedures/GEN-13.yaml +70 -0
  77. cactus_test_definitions/client/procedures/LOA-01.yaml +73 -0
  78. cactus_test_definitions/client/procedures/LOA-02.yaml +73 -0
  79. cactus_test_definitions/client/procedures/LOA-03.yaml +160 -0
  80. cactus_test_definitions/client/procedures/LOA-04.yaml +161 -0
  81. cactus_test_definitions/client/procedures/LOA-05.yaml +89 -0
  82. cactus_test_definitions/client/procedures/LOA-06.yaml +89 -0
  83. cactus_test_definitions/client/procedures/LOA-07.yaml +145 -0
  84. cactus_test_definitions/client/procedures/LOA-08.yaml +145 -0
  85. cactus_test_definitions/client/procedures/LOA-09.yaml +117 -0
  86. cactus_test_definitions/client/procedures/LOA-10.yaml +741 -0
  87. cactus_test_definitions/client/procedures/LOA-11.yaml +378 -0
  88. cactus_test_definitions/client/procedures/LOA-12.yaml +378 -0
  89. cactus_test_definitions/client/procedures/LOA-13.yaml +71 -0
  90. cactus_test_definitions/client/procedures/MUL-01.yaml +92 -0
  91. cactus_test_definitions/client/procedures/MUL-02.yaml +80 -0
  92. cactus_test_definitions/client/procedures/MUL-03.yaml +74 -0
  93. cactus_test_definitions/client/test_procedures.py +185 -0
  94. cactus_test_definitions/client/validate.py +98 -0
  95. cactus_test_definitions/csipaus.py +81 -0
  96. cactus_test_definitions/errors.py +15 -0
  97. cactus_test_definitions/parameters.py +153 -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/__init__.py +23 -0
  102. cactus_test_definitions/server/__pycache__/__init__.cpython-312.pyc +0 -0
  103. cactus_test_definitions/server/__pycache__/actions.cpython-312.pyc +0 -0
  104. cactus_test_definitions/server/__pycache__/checks.cpython-312.pyc +0 -0
  105. cactus_test_definitions/server/__pycache__/test_procedures.cpython-312-pytest-8.3.5.pyc +0 -0
  106. cactus_test_definitions/server/__pycache__/validate.cpython-312.pyc +0 -0
  107. cactus_test_definitions/server/actions.py +139 -0
  108. cactus_test_definitions/server/checks.py +117 -0
  109. cactus_test_definitions/server/procedures/S-ALL-01.yaml +42 -0
  110. cactus_test_definitions/server/procedures/S-ALL-02.yaml +65 -0
  111. cactus_test_definitions/server/procedures/S-ALL-03.yaml +65 -0
  112. cactus_test_definitions/server/procedures/S-ALL-04.yaml +137 -0
  113. cactus_test_definitions/server/procedures/S-ALL-05.yaml +111 -0
  114. cactus_test_definitions/server/procedures/S-OPT-01.yaml +42 -0
  115. cactus_test_definitions/server/procedures/S-OPT-02.yaml +40 -0
  116. cactus_test_definitions/server/procedures/S-OPT-03.yaml +44 -0
  117. cactus_test_definitions/server/procedures/S-OPT-04.yaml +32 -0
  118. cactus_test_definitions/server/test_procedures.py +122 -0
  119. cactus_test_definitions/server/validate.py +30 -0
  120. cactus_test_definitions/variable_expressions.py +419 -0
  121. cactus_test_definitions-1.6.0.dist-info/METADATA +289 -0
  122. cactus_test_definitions-1.6.0.dist-info/RECORD +139 -0
  123. cactus_test_definitions-1.6.0.dist-info/WHEEL +5 -0
  124. cactus_test_definitions-1.6.0.dist-info/licenses/LICENSE.txt +22 -0
  125. cactus_test_definitions-1.6.0.dist-info/top_level.txt +2 -0
  126. tests/__init__.py +0 -0
  127. tests/unit/__init__.py +0 -0
  128. tests/unit/client/__init__.py +0 -0
  129. tests/unit/client/test_actions.py +72 -0
  130. tests/unit/client/test_checks.py +71 -0
  131. tests/unit/client/test_events.py +36 -0
  132. tests/unit/client/test_test_procedures.py +103 -0
  133. tests/unit/client/test_validate.py +153 -0
  134. tests/unit/server/__init__.py +0 -0
  135. tests/unit/server/test_test_procedures.py +60 -0
  136. tests/unit/server/test_validate.py +62 -0
  137. tests/unit/test_csipaus.py +49 -0
  138. tests/unit/test_parameters.py +197 -0
  139. tests/unit/test_variable_expressions.py +402 -0
@@ -0,0 +1,170 @@
1
+
2
+ # Server Test Schema
3
+
4
+ This is for the **SERVER** test procedures.
5
+
6
+ At its most basic level, a server test is a series of actions by a virtual "client" that will probe the server and look for unexpected behaviour/responses.
7
+
8
+ ## Clients and Context
9
+
10
+ Each test will define one or more "virtual clients" as a precondition. These can be restricted to a specific client type (eg Aggregator or Device) or left unspecified.
11
+
12
+ At the beginning of each test, the client will only know it's LFDI, PEN, PIN and certificate details. Through running actions it will discover resources and populate a local "context" which represents the last seen CSIP-Aus resources. Each client's context is seperate, so if Client A performs discovery, those resources will be invisible to Client B.
13
+
14
+ ## Steps Schema
15
+
16
+ The most basic building block of a server `TestProcedure` is a `Step`. Each `Step` will always define a single `Action` which dictates the behaviour of a virtual client (eg sending a particular request) which is then followed by a series of `Check` objects to evaluate. In order for a step to pass it must:
17
+
18
+ 1) Execute with any errors (eg - able to successfully make a HTTP request to the utility server)
19
+ 2) Have all checks return passed
20
+
21
+
22
+ Steps:
23
+ - id: DISCOVERY
24
+ action:
25
+ type: discovery
26
+ parameters:
27
+ resources:
28
+ - DeviceCapability
29
+ - Time
30
+ - MirrorUsagePointList
31
+ - EndDevice
32
+ - DER
33
+ checks:
34
+ - type: discovered
35
+ parameters:
36
+ resources:
37
+ - DeviceCapability
38
+ - Time
39
+ - MirrorUsagePointList
40
+ - EndDevice
41
+ - DER
42
+ links:
43
+ - ConnectionPoint
44
+ - Registration
45
+ - DERCapability
46
+ - DERSettings
47
+ - DERStatus
48
+ - type: end-device
49
+ parameters:
50
+ matches_client: true
51
+ - type: time-synced
52
+
53
+
54
+ Step Schema:
55
+ ```
56
+ Steps:
57
+ - DESCRIPTIVE_TITLE_OF_STEP: # This is used for display
58
+ action: #
59
+ type: # string identifier of the action type - see table below
60
+ parameters: # Any parameters to modify the default behaviour of the action - see table below
61
+ checks: # A list of Check definitions that will need to be true for this event to trigger - see section on Checks below
62
+ - type: # string identifier of the check type - see table below
63
+ parameters: # Any parameters to modify the default behaviour of the check - see table below
64
+
65
+ client: # The string descriptor of the "Required Client" in the preconditions that will execute this step
66
+ # (Defaults to the first required client)
67
+
68
+ use_client_context: # The string descriptor of the "Required Client" in the preconditions whose context/memory
69
+ # of discovered resources will be used (for testing cross client authentication issues)
70
+ instructions: # List of text strings to render while this test is executing
71
+ repeat_until_pass: # Most steps if failed will abort the test, setting this to true will repeat this step regularly
72
+ # until a pass is recorded (eg - use it to prompt a server to inject a DERControl)
73
+ ```
74
+
75
+
76
+ ### Actions
77
+
78
+ These are the currently defined `Action` elements that can be included in a test.
79
+
80
+ This is an example of an `Action` elements that trigger a client to requests links from the device capability URI until all nominated resources are reached.
81
+
82
+ ```
83
+ action:
84
+ type: discovery
85
+ parameters:
86
+ resources:
87
+ - Time
88
+ - MirrorUsagePointList
89
+ - EndDevice
90
+ - DER
91
+ ```
92
+
93
+
94
+ | **name** | **params** | **description** |
95
+ | -------- | ---------- | --------------- |
96
+ | `discovery` | `resources: list[CSIPAusResource]` `next_polling_window: bool/None` | Performs a full discovery / refresh of the client's context from DeviceCapability downwards, looking to discover the specific resources. Can be delayed until the next polling window. |
97
+ | `notifications` | `collect: bool` `disable: bool` | If `collect`, consumes subscription notifications and inserts them into the current context, if `disable` causes the subscription notification webhook to simulate an outage (return HTTP 5XX) |
98
+ | `wait` | `duration_seconds: int` | Performs no action for the nominated period of time |
99
+ | `refresh-resource` | `resource: CSIPAusResource` `expect_rejection: bool/None` `expect_rejection_or_empty: bool/None` | Forces a particular resource to be refreshed (using existing hrefs in context). Can be set to expect a HTTP 4XX ErrorResponse and/or an empty list resource (if appropriate). |
100
+ | `insert-end-device` | `force_lfdi: str/None` `expect_rejection: bool/None` | Causes the client to submit a new EndDevice registration and resolves the returned Location header |
101
+ | `upsert-connection-point` | `connectionPointId: str` `expect_rejection: bool/None` | Causes the client to submit a new ConnectionPoint with ID for the client's EndDevice |
102
+ | `upsert-mup` | `mup_id: str` `location: CSIPAusReadingLocation` `reading_types: list[CSIPAusReadingType]` `mmr_mrids: list[str]/None` `pow10_multiplier: int/None` `expect_rejection: bool/None` | Submits a MirrorUsagePoint with MirrorMeterReading's. Will ensure stable MRID values for the same sets of parameters (unless overridden with mmr_mrids). `mup_id` will alias this MirrorUsagePoint for future action calls. |
103
+ | `insert-readings` | `mup_id: str` `values: ReadingTypeValues` `expect_rejection: bool/None` | Begins the submission of readings (at MUP post rate, to an earlier call to `upsert-mup` with the same `mup_id`). Will interleave transmission with subsequent steps (non blocking) if multiple sets of values are specified |
104
+ | `upsert-der-status` | `genConnectStatus: int/None` `operationalModeStatus: int/None` `alarmStatus: int/None` `expect_rejection: bool/None` | Sends DERStatus - validates that the server persisted the values correctly |
105
+ | `upsert-der-capability` | `type: int` `rtgMaxW: int` `modesSupported: int` `doeModesSupported: int` | Sends DERCapability - validates that the server persisted the values correctly |
106
+ | `upsert-der-settings` | `setMaxW: int` `setGradW: int` `modesEnabled: int` `doeModesEnabled: int` | Sends DERSettings - validates that the server persisted the values correctly |
107
+ | `send-malformed-der-settings` | `updatedTime_missing: bool` `modesEnabled_int: bool` | Sends a malformed DERSettings - expects a failure and that the server will NOT change anything |
108
+ | `send-malformed-response` | `mrid_unknown: bool` `endDeviceLFDI_unknown: bool` `response_invalid: bool` | Sends a malformed Response (using the most recent DERControl replyTo) - expects a failure response |
109
+ | `create-subscription` | `sub_id: str` `resource: CSIPAusResource` | Sends a new Subscription - validates that the server persisted the values correctly via Location. `sub_id` will alias this subscription for future action calls. |
110
+ | `delete-subscription` | `sub_id: str` | Sends a deletion for a previously created Subscription. |
111
+ | `respond-der-controls` | None | Enumerates all known DERControls and sends a Response for any that require it. |
112
+
113
+
114
+ ### Checks
115
+
116
+ A `Check` is a boolean test of what the client has in its current context. They are typically defined as a success/failure condition to be run at the end of a `Step`.
117
+
118
+ | **name** | **params** | **description** |
119
+ | -------- | ---------- | --------------- |
120
+ | `discovered` | `resources: list[CSIPAusResource]` `links: list[CSIPAusResource]` | Does the client's context have the nominated resources (or the parent resource with an appropriate link). |
121
+ | `time-sync` | None | Does the client have a TimeResponse and does it closely map to the client's local time. |
122
+ | `end-device` | `matches_client: bool` `matches_pin` | Is there an EndDevice that matches the client's LFDI (can be negatively asserted) and does it have a specific Registration PIN |
123
+ | `der-program` | `minimum_count: int/None` `maximum_count: int/None` `primacy: int/None` `fsa_index: int/None` | Are there enough DERProgram(s) that satisfy the filter criteria? `fsa_index` matches DERPrograms that belong to the nth (0 based) FunctionSetAssignment |
124
+ | `der-control` | `minimum_count: int/None` `maximum_count: int/None` `latest: bool/None` `opModImpLimW: float/None` `opModExpLimW: float/None` `opModLoadLimW: float/None` `opModGenLimW: float/None` `opModEnergize: bool/None` `opModConnect: bool/None` `opModFixedW: float/None` `rampTms: int/None` `randomizeStart: int/None` `event_status: int/None` `responseRequired: int/None` `derp_primacy: int/None` | Are there enough DERProgram(s) that satisfy the filter criteria? `latest` will ONLY match the most recent DERControl. |
125
+ | `default-der-control` | `opModExpLimW: float/None` `opModLoadLimW: float/None` `opModGenLimW: float/None` `setGradW: int/None` | matches any DefaultDERControl with the specified values |
126
+ | `mirror-usage-point` | `matches: bool` `location: CSIPAusReadingLocation/None` `reading_types: list[CSIPAusReadingType]/None` `mmr_mrids: list[str]/None` `post_rate_seconds: int/None` | Does a MirrorUsagePoint exist with the specified values (or not exist if `matches` is false). Only asserts specified values. |
127
+ | `subscription` | `matches: bool` `resource: CSIPAusResource`| Does a Subscription exist for the specified resource (or not exist if `matches` is false). |
128
+ | `poll-rate` | `resource: CSIPAusResource` `poll_rate_seconds: int` | Does the nominated resource have the specified poll rate. |
129
+
130
+
131
+ ### Parameter Variable Resolution
132
+
133
+ Any `parameter` element expects a series of name/value pairs to pass to the "parent" `Action` or `Check` . For example:
134
+
135
+ ```
136
+ parameters:
137
+ number_param: 123
138
+ text_param: Text Content
139
+ date_param: 2020-01-02 03:04:05Z
140
+ csip_aus_resource: EndDeviceList
141
+
142
+ ```
143
+
144
+ But placeholder variables may also be used to reference things that aren't known until the test is underway. For example, the following would instead set `number_param` to the current setMaxW supplied by the client while `date_param` would be set to the moment in time that the `Action`, `Check` or `Event` is being evaluated.
145
+
146
+ ```
147
+ parameters:
148
+ number_param: $setMaxW
149
+ text_param: Text Content
150
+ date_param: $now
151
+
152
+ ```
153
+
154
+ The following are all the `NamedVariable` types currently implemented (these are distinct from the named variables
155
+ defined in the client test procedures)
156
+
157
+ | **name** | **description** |
158
+ | -------- | --------------- |
159
+ | `$now` | Resolves to the current moment in time (timezone aware). Returns a datetime |
160
+ | `$setMaxW` | Resolves to the current client configuration value for `DERSetting.setMaxW` as a number. |
161
+
162
+
163
+ Expressions are also supported.
164
+
165
+ ```
166
+ parameters:
167
+ number_param: $(setMaxW / 2)
168
+ text_param: Text Content
169
+ date_param: $(now - '5 mins')
170
+ ```
@@ -0,0 +1,23 @@
1
+ from cactus_test_definitions.server.actions import ACTION_PARAMETER_SCHEMA, Action
2
+ from cactus_test_definitions.server.checks import CHECK_PARAMETER_SCHEMA, Check
3
+ from cactus_test_definitions.server.test_procedures import (
4
+ Preconditions,
5
+ Step,
6
+ TestProcedure,
7
+ TestProcedureId,
8
+ get_all_test_procedures,
9
+ get_test_procedure,
10
+ )
11
+
12
+ __all__ = [
13
+ "TestProcedureId",
14
+ "Action",
15
+ "ACTION_PARAMETER_SCHEMA",
16
+ "Check",
17
+ "CHECK_PARAMETER_SCHEMA",
18
+ "Step",
19
+ "Preconditions",
20
+ "TestProcedure",
21
+ "get_all_test_procedures",
22
+ "get_test_procedure",
23
+ ]
@@ -0,0 +1,139 @@
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
+ client: str | None = None # use the client with this id to execute this action. If None, use the 0th client
20
+ parameters: dict[str, Any] = None # type: ignore # This will be forced in __post_init__
21
+
22
+ def __post_init__(self):
23
+ """Some parameter values might contain variable expressions (eg: a string "$now") that needs to be replaced
24
+ with an parsed Expression object instead."""
25
+ if self.parameters is None:
26
+ self.parameters = {}
27
+
28
+ for k, v in self.parameters.items():
29
+ variable_expr = try_extract_variable_expression(v)
30
+ if variable_expr:
31
+ self.parameters[k] = parse_variable_expression_body(variable_expr, k)
32
+
33
+
34
+ # The parameter schema for each action, keyed by the action name
35
+ ACTION_PARAMETER_SCHEMA: dict[str, dict[str, ParameterSchema]] = {
36
+ "discovery": {
37
+ "resources": ParameterSchema(True, ParameterType.ListCSIPAusResource), # What resources to try and resolve?
38
+ "next_polling_window": ParameterSchema(
39
+ False, ParameterType.Boolean
40
+ ), # If set - delay this until the upcoming polling window (eg- wait for the next whole minute)
41
+ }, # Performs a full discovery / refresh of the client's context from DeviceCapability downwards
42
+ "notifications": {
43
+ "collect": ParameterSchema(
44
+ False, ParameterType.Boolean
45
+ ), # Collects latest subscription notifications into context
46
+ "disable": ParameterSchema(False, ParameterType.Boolean), # Simulates HTTP 5XX outage at the endpoint
47
+ },
48
+ "wait": {
49
+ "duration_seconds": ParameterSchema(True, ParameterType.Integer)
50
+ }, # Waits (doing nothing - blocking other step actions) until the specified time period has passed
51
+ "comms-status": {
52
+ "notifications_enabled": ParameterSchema(True, ParameterType.Boolean) # Enable/Disble notification webhook
53
+ }, # Enables or disables certain communications
54
+ "refresh-resource": {
55
+ "resource": ParameterSchema(True, ParameterType.CSIPAusResource),
56
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # if set - expect 4XX and ErrorPayload
57
+ "expect_rejection_or_empty": ParameterSchema(
58
+ False, ParameterType.Boolean
59
+ ), # Similar to expect_rejection but also allow en empty list (if it's a list resource)
60
+ }, # Force an existing resource (in the client's context) to be re-fetched via href. Updates context on success
61
+ "insert-end-device": {
62
+ "force_lfdi": ParameterSchema(False, ParameterType.String), # Forces the use of this LFDI
63
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # If set - expect 4XX and ErrorPayload
64
+ }, # Inserts an EndDevice and then validates the returned Location header
65
+ "upsert-connection-point": {
66
+ "connectionPointId": ParameterSchema(True, ParameterType.String),
67
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # If set - expect ErrorPayload reasonCode 1
68
+ },
69
+ "upsert-mup": {
70
+ "mup_id": ParameterSchema(True, ParameterType.String), # Used to alias the returned MUP ID
71
+ "location": ParameterSchema(True, ParameterType.CSIPAusReadingLocation),
72
+ "reading_types": ParameterSchema(True, ParameterType.ListCSIPAusReadingType),
73
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # If set - expect 4XX and ErrorPayload
74
+ "mmr_mrids": ParameterSchema(
75
+ False, ParameterType.ListString
76
+ ), # Must correspond 1-1 with reading_types. Used for forcing specific mrid values
77
+ "pow10_multiplier": ParameterSchema(
78
+ False, ParameterType.Integer
79
+ ), # Force the use a particular pow10. Defaults to 0 otherwise
80
+ }, # Register a MUP with the specified values. MMR's based on hash of current client / reading types
81
+ "insert-readings": {
82
+ "mup_id": ParameterSchema(True, ParameterType.String), # Must be previously defined with register-mup
83
+ "values": ParameterSchema(
84
+ True, ParameterType.ReadingTypeValues
85
+ ), # The sequences of values to send at the MUP post rate
86
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # If set - expect 4XX and ErrorPayload
87
+ }, # Sends readings - validates that the telemetry is parsed correctly by the server
88
+ "upsert-der-status": {
89
+ "genConnectStatus": ParameterSchema(False, ParameterType.Integer),
90
+ "operationalModeStatus": ParameterSchema(False, ParameterType.Integer),
91
+ "alarmStatus": ParameterSchema(False, ParameterType.Integer),
92
+ "expect_rejection": ParameterSchema(False, ParameterType.Boolean), # If set - expect 4XX and ErrorPayload
93
+ }, # Sends DERStatus - validates that the server persisted the values correctly
94
+ "upsert-der-capability": {
95
+ "type": ParameterSchema(True, ParameterType.Integer),
96
+ "rtgMaxW": ParameterSchema(True, ParameterType.Integer),
97
+ "modesSupported": ParameterSchema(True, ParameterType.Integer),
98
+ "doeModesSupported": ParameterSchema(True, ParameterType.Integer),
99
+ }, # Sends DERCapability - validates that the server persisted the values correctly
100
+ "upsert-der-settings": {
101
+ "setMaxW": ParameterSchema(True, ParameterType.Integer),
102
+ "setGradW": ParameterSchema(True, ParameterType.Integer),
103
+ "modesEnabled": ParameterSchema(True, ParameterType.Integer),
104
+ "doeModesEnabled": ParameterSchema(True, ParameterType.Integer),
105
+ }, # Sends DERSettings - validates that the server persisted the values correctly
106
+ "send-malformed-der-settings": {
107
+ "updatedTime_missing": ParameterSchema(True, ParameterType.Boolean), # If true - updatedTime will be stripped
108
+ "modesEnabled_int": ParameterSchema(True, ParameterType.Boolean), # If true - modesEnabled will send an int
109
+ }, # Sends a malformed DERSettings - expects a failure and that the server will NOT change anything
110
+ "send-malformed-response": {
111
+ "mrid_unknown": ParameterSchema(True, ParameterType.Boolean), # If true - mrid will be random
112
+ "endDeviceLFDI_unknown": ParameterSchema(True, ParameterType.Boolean), # If true - endDeviceLfdi will be random
113
+ "response_invalid": ParameterSchema(True, ParameterType.Boolean), # If true - response will be a reserved value
114
+ }, # Sends a malformed Response (using the most recent DERControl replyTo) - expects a failure response
115
+ "create-subscription": {
116
+ "sub_id": ParameterSchema(True, ParameterType.String), # Used to alias the returned subscription ID
117
+ "resource": ParameterSchema(True, ParameterType.CSIPAusResource),
118
+ }, # Sends a new Subscription - validates that the server persisted the values correctly via Location
119
+ "delete-subscription": {
120
+ "sub_id": ParameterSchema(True, ParameterType.String), # Must match a previously
121
+ }, # Sends a Subscription deletion
122
+ "respond-der-controls": {}, # Enumerates all known DERControls and sends a Response for any that require it
123
+ }
124
+ VALID_ACTION_NAMES: set[str] = set(ACTION_PARAMETER_SCHEMA.keys())
125
+
126
+
127
+ def validate_action_parameters(procedure_name: str, step_name: str, action: Action) -> None:
128
+ """Validates the action parameters for the parent TestProcedure based on the ACTION_PARAMETER_SCHEMA
129
+
130
+ raises TestProcedureDefinitionError on failure"""
131
+ location = f"{procedure_name}.step[{step_name}]" # Descriptive location
132
+
133
+ parameter_schema = ACTION_PARAMETER_SCHEMA.get(action.type, None)
134
+ if parameter_schema is None:
135
+ raise TestProcedureDefinitionError(
136
+ f"{location} has an invalid action name '{action.type}'. Valid Names: {VALID_ACTION_NAMES}"
137
+ )
138
+
139
+ 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 a Test Step and provides a pass/fail result with a
19
+ description. It will typically inspect the state of the client based on what it has seen from the server
20
+
21
+ eg: Ensuring that the client was able to see an EndDevice registration"""
22
+
23
+ type: str
24
+ parameters: dict[str, Any] = None # type: ignore # This will be forced in __post_init__
25
+
26
+ def __post_init__(self):
27
+ """Some parameter values might contain variable expressions (eg: a string "$now") that needs to be replaced
28
+ with an parsed Expression object instead."""
29
+ if self.parameters is None:
30
+ self.parameters = {}
31
+ for k, v in self.parameters.items():
32
+ variable_expr = try_extract_variable_expression(v)
33
+ if variable_expr:
34
+ self.parameters[k] = parse_variable_expression_body(variable_expr, k)
35
+
36
+
37
+ # The parameter schema for each action, keyed by the action name
38
+ CHECK_PARAMETER_SCHEMA: dict[str, dict[str, ParameterSchema]] = {
39
+ "discovered": {
40
+ "resources": ParameterSchema(False, ParameterType.ListCSIPAusResource),
41
+ "links": ParameterSchema(False, ParameterType.ListCSIPAusResource),
42
+ },
43
+ "time-synced": {}, # Passes if the current Time resource is synced with this client's date/time
44
+ "end-device": {
45
+ "matches_client": ParameterSchema(
46
+ True, ParameterType.Boolean
47
+ ), # assert the existence / non existence of an EndDevice for the current client
48
+ "matches_pin": ParameterSchema(
49
+ False, ParameterType.Boolean
50
+ ), # if set - The matches_client criteria will ALSO check the registration PIN for the EndDevice. Default False
51
+ },
52
+ "der-program": {
53
+ "minimum_count": ParameterSchema(False, ParameterType.Integer), # Needs at least this many derps to pass
54
+ "maximum_count": ParameterSchema(False, ParameterType.Integer), # Needs at most this many derps to pass
55
+ "primacy": ParameterSchema(False, ParameterType.Integer), # Filters derps based on this primacy value
56
+ "fsa_index": ParameterSchema(
57
+ False, ParameterType.Integer
58
+ ), # Filters derps that belong to the nth (0 based) FunctionSetAssignment index
59
+ },
60
+ "der-control": {
61
+ "minimum_count": ParameterSchema(False, ParameterType.Integer), # Needs at least this many controls to pass
62
+ "maximum_count": ParameterSchema(False, ParameterType.Integer), # Needs at most this many controls to pass
63
+ "latest": ParameterSchema(False, ParameterType.Boolean), # forces filter checks against the most recent control
64
+ "opModImpLimW": ParameterSchema(False, ParameterType.Float), # Filters controls based on this value
65
+ "opModExpLimW": ParameterSchema(False, ParameterType.Float), # Filters controls based on this value
66
+ "opModLoadLimW": ParameterSchema(False, ParameterType.Float), # Filters controls based on this value
67
+ "opModGenLimW": ParameterSchema(False, ParameterType.Float), # Filters controls based on this value
68
+ "opModEnergize": ParameterSchema(False, ParameterType.Boolean), # Filters controls based on this value
69
+ "opModConnect": ParameterSchema(False, ParameterType.Boolean), # Filters controls based on this value
70
+ "opModFixedW": ParameterSchema(False, ParameterType.Float), # Filters controls based on this value
71
+ "rampTms": ParameterSchema(False, ParameterType.Integer), # Filter on this val. 0 means negative assertion
72
+ "randomizeStart": ParameterSchema(False, ParameterType.Integer), # Filter on this val (in seconds)
73
+ "event_status": ParameterSchema(False, ParameterType.Integer), # Filter on Event.status value
74
+ "responseRequired": ParameterSchema(False, ParameterType.Integer), # Filter on responseRequired value
75
+ "derp_primacy": ParameterSchema(
76
+ False, ParameterType.Integer
77
+ ), # Filter to control's belonging to a DERProgram with this primacy value
78
+ }, # Matches many DERControls (specified by minimum_count) against additional other filter criteria
79
+ "default-der-control": {
80
+ "opModImpLimW": ParameterSchema(False, ParameterType.Float),
81
+ "opModExpLimW": ParameterSchema(False, ParameterType.Float),
82
+ "opModGenLimW": ParameterSchema(False, ParameterType.Float),
83
+ "opModLoadLimW": ParameterSchema(False, ParameterType.Float),
84
+ "setGradW": ParameterSchema(False, ParameterType.Integer), # Hundredths of a percent / second
85
+ }, # matches any DefaultDERControl with the specified values
86
+ "mirror-usage-point": {
87
+ "matches": ParameterSchema(True, ParameterType.Boolean), # True for positive assert, False for negative assert
88
+ "location": ParameterSchema(False, ParameterType.CSIPAusReadingLocation), # If not specified - match anything
89
+ "reading_types": ParameterSchema(False, ParameterType.ListCSIPAusReadingType), # If not specified - match all
90
+ "mmr_mrids": ParameterSchema(
91
+ False, ParameterType.ListString
92
+ ), # Must correspond 1-1 with reading_types. Used for forcing specific mrid values
93
+ "post_rate_seconds": ParameterSchema(False, ParameterType.Integer), # Only asserted if specified
94
+ }, # True if the matches assertion finds a MirrorUsagePoint with the specified parameters (requires exact match)
95
+ "subscription": {
96
+ "matches": ParameterSchema(True, ParameterType.Boolean), # True for positive assert, False for negative assert
97
+ "resource": ParameterSchema(True, ParameterType.CSIPAusResource),
98
+ }, # Matches the existence/nonexistence of a subscription for the specified resource
99
+ "poll-rate": {
100
+ "resource": ParameterSchema(True, ParameterType.CSIPAusResource),
101
+ "poll_rate_seconds": ParameterSchema(True, ParameterType.Integer),
102
+ }, # Asserts a specific poll rate value
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,42 @@
1
+ Description: Discovery with Out-Of-Band registration
2
+ Category: Registration
3
+ Classes:
4
+ - A
5
+
6
+ TargetVersions:
7
+ - v1.2
8
+
9
+ Preconditions:
10
+ required_clients:
11
+ - id: client
12
+
13
+ Steps:
14
+ - id: DISCOVERY
15
+ action:
16
+ type: discovery
17
+ parameters:
18
+ resources:
19
+ - DeviceCapability
20
+ - Time
21
+ - MirrorUsagePointList
22
+ - EndDevice
23
+ - DER
24
+ checks:
25
+ - type: discovered
26
+ parameters:
27
+ resources:
28
+ - DeviceCapability
29
+ - Time
30
+ - MirrorUsagePointList
31
+ - EndDevice
32
+ - DER
33
+ links:
34
+ - ConnectionPoint
35
+ - Registration
36
+ - DERCapability
37
+ - DERSettings
38
+ - DERStatus
39
+ - type: end-device
40
+ parameters:
41
+ matches_client: true
42
+ - type: time-synced
@@ -0,0 +1,65 @@
1
+ Description: Discovery with In-Band Registration for Direct Clients
2
+ Category: Registration
3
+ Classes:
4
+ - A
5
+
6
+ TargetVersions:
7
+ - v1.2
8
+
9
+ Preconditions:
10
+ required_clients:
11
+ - id: client
12
+ client_type: device
13
+
14
+ Steps:
15
+ - id: PRECONDITION
16
+ repeat_until_pass: true
17
+ instructions:
18
+ - Remove any existing EndDevice registrations for client.
19
+ action:
20
+ type: discovery
21
+ parameters:
22
+ resources:
23
+ - DeviceCapability
24
+ - Time
25
+ - MirrorUsagePointList
26
+ - EndDevice
27
+ - DER
28
+ checks:
29
+ - type: discovered
30
+ parameters:
31
+ resources:
32
+ - DeviceCapability
33
+ - Time
34
+ - MirrorUsagePointList
35
+ - EndDeviceList
36
+ - type: end-device # This will be the check that will block if there is an existing EndDevice registration
37
+ parameters:
38
+ matches_client: false
39
+ - type: time-synced
40
+
41
+ - id: REGISTER
42
+ action:
43
+ type: insert-end-device
44
+ checks:
45
+ - type: end-device
46
+ parameters:
47
+ matches_client: true
48
+
49
+ - id: FETCHING DER INFO
50
+ repeat_until_pass: true
51
+ instructions:
52
+ - Populate DERList with an entry that has a DERCapabilityLink, DERSettingsLink and DERStatusLink.
53
+ action:
54
+ type: discovery
55
+ parameters:
56
+ resources:
57
+ - DER
58
+ checks:
59
+ - type: discovered
60
+ parameters:
61
+ links:
62
+ - DERCapability
63
+ - DERSettings
64
+ - DERStatus
65
+
@@ -0,0 +1,65 @@
1
+ Description: Discovery with In-Band Registration for Aggregator Clients
2
+ Category: Registration
3
+ Classes:
4
+ - A
5
+
6
+ TargetVersions:
7
+ - v1.2
8
+
9
+ Preconditions:
10
+ required_clients:
11
+ - id: client
12
+ client_type: aggregator
13
+
14
+ Steps:
15
+ - id: PRECONDITION
16
+ repeat_until_pass: true
17
+ instructions:
18
+ - Remove any existing EndDevice registrations for client.
19
+ action:
20
+ type: discovery
21
+ parameters:
22
+ resources:
23
+ - DeviceCapability
24
+ - Time
25
+ - MirrorUsagePointList
26
+ - EndDevice
27
+ - DER
28
+ checks:
29
+ - type: discovered
30
+ parameters:
31
+ resources:
32
+ - DeviceCapability
33
+ - Time
34
+ - MirrorUsagePointList
35
+ - EndDeviceList
36
+ - type: end-device # This will be the check that will block if there is an existing EndDevice registration
37
+ parameters:
38
+ matches_client: false
39
+ - type: time-synced
40
+
41
+ - id: REGISTER
42
+ action:
43
+ type: insert-end-device
44
+ checks:
45
+ - type: end-device
46
+ parameters:
47
+ matches_client: true
48
+
49
+ - id: FETCHING DER INFO
50
+ repeat_until_pass: true
51
+ instructions:
52
+ - Populate DERList with an entry that has a DERCapabilityLink, DERSettingsLink and DERStatusLink.
53
+ action:
54
+ type: discovery
55
+ parameters:
56
+ resources:
57
+ - DER
58
+ checks:
59
+ - type: discovered
60
+ parameters:
61
+ links:
62
+ - DERCapability
63
+ - DERSettings
64
+ - DERStatus
65
+