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,402 @@
1
+ from datetime import datetime, timedelta
2
+ from decimal import Decimal
3
+ from typing import Any
4
+
5
+ import pytest
6
+ from cactus_test_definitions.client.test_procedures import Action
7
+ from cactus_test_definitions.variable_expressions import (
8
+ Constant,
9
+ Expression,
10
+ NamedVariable,
11
+ NamedVariableType,
12
+ OperationType,
13
+ UnparseableVariableExpressionError,
14
+ has_named_variable,
15
+ is_resolvable_variable,
16
+ parse_time_delta,
17
+ parse_variable_expression_body,
18
+ try_extract_variable_expression,
19
+ snake_to_camel,
20
+ named_variable_repr,
21
+ operation_repr,
22
+ BaseExpression,
23
+ )
24
+
25
+
26
+ @pytest.mark.parametrize(
27
+ "body, expected",
28
+ [
29
+ (None, None),
30
+ (123, None),
31
+ (12.3, None),
32
+ (["$no_list_inspection"], None),
33
+ ({"$no_dict_inspection": "$no_dict_inspection"}, None),
34
+ (datetime(2001, 2, 3), None),
35
+ ("", None),
36
+ (" ", None),
37
+ ("$now", "now"),
38
+ (" $now\n ", "now"),
39
+ ("$(now)", "now"),
40
+ ("$(now - '5 minutes')", "now - '5 minutes'"),
41
+ ("$( now - '5 minutes' )", " now - '5 minutes' "),
42
+ ("\\$(now)", None), # escaped
43
+ (" \\$(now) ", None), # escaped
44
+ (" \\$now ", None), # escaped
45
+ ("\\$now", None), # escaped
46
+ ("$variable_with_556", "variable_with_556"),
47
+ ("$(variable_with_556)", "variable_with_556"),
48
+ ("$variable-556", ValueError), # Don't allow "non variable" data to get omitted
49
+ ("$(variable-556)", "variable-556"),
50
+ ("$longer_variable no spaces", ValueError),
51
+ ("$(longer_variable but include everything)", "longer_variable but include everything"),
52
+ ("$(now", ValueError), # Unclosed bracket
53
+ (" $(now ", ValueError), # Unclosed bracket
54
+ (" $(now foo", ValueError), # Unclosed bracket
55
+ ("$ invalid_space", ValueError),
56
+ ("$-invalid_char", ValueError),
57
+ ("$", ValueError), # No variable body included
58
+ ("$ ", ValueError), # No variable body included
59
+ ("\\$", None),
60
+ ("$()", ValueError), # No variable body included
61
+ ("\\$()", None),
62
+ (" \\$() ", None),
63
+ ("$(valid) no trailing data", ValueError), # A string is either a variable or not. Can't substring variables
64
+ ("no leading data $(valid)", ValueError), # A string is either a variable or not. Can't substring variables
65
+ ],
66
+ )
67
+ def test_try_extract_variable_expression(body: Any, expected: str | None | type[Exception]):
68
+ """Various test cases that try expose edge cases in try_extract_variable_expression"""
69
+ if isinstance(expected, type):
70
+ with pytest.raises(expected):
71
+ try_extract_variable_expression(body)
72
+ else:
73
+ actual = try_extract_variable_expression(body)
74
+ assert actual is None or isinstance(actual, str)
75
+ assert actual == expected, f"Input string: '{body}'"
76
+
77
+
78
+ @pytest.mark.parametrize(
79
+ "unquoted_raw, expected",
80
+ [
81
+ ("", UnparseableVariableExpressionError),
82
+ ("5 mins", timedelta(minutes=5)),
83
+ ("-5 mins", timedelta(minutes=-5)),
84
+ ("5.23 mins", timedelta(minutes=5.23)),
85
+ ("-5.23 mins", timedelta(minutes=-5.23)),
86
+ ("-0.03 days", timedelta(days=-0.03)),
87
+ ("-0.03 days", timedelta(days=-0.03)),
88
+ ("-0.03days", timedelta(days=-0.03)),
89
+ ("4 minutes", timedelta(minutes=4)),
90
+ ("4 minute", timedelta(minutes=4)),
91
+ ("0 minute", timedelta(minutes=0)),
92
+ ("4 hours", timedelta(hours=4)),
93
+ ("4 hour", timedelta(hours=4)),
94
+ ("4 hrs", timedelta(hours=4)),
95
+ ("4 hr", timedelta(hours=4)),
96
+ ("4 days", timedelta(days=4)),
97
+ ("4 day", timedelta(days=4)),
98
+ ("4 seconds", timedelta(seconds=4)),
99
+ ("4 second", timedelta(seconds=4)),
100
+ ("4 secs", timedelta(seconds=4)),
101
+ ("4 sec", timedelta(seconds=4)),
102
+ ("4 foo", UnparseableVariableExpressionError), # Unknown unit
103
+ ("4-.2 foo", UnparseableVariableExpressionError), # Not a number
104
+ ("4.2.3 foo", UnparseableVariableExpressionError), # Not a number
105
+ ],
106
+ )
107
+ def test_parse_time_delta(unquoted_raw: str, expected: timedelta | type[Exception]):
108
+ """Tests the various ways a time delta interval can be encoded (and the various ways it can fail)"""
109
+ VALID_QUOTE_CHARS = ["'", '"']
110
+
111
+ # Check weird edge cases from unquoted intervals or badly quoted intervals
112
+ with pytest.raises(UnparseableVariableExpressionError):
113
+ parse_time_delta(unquoted_raw)
114
+
115
+ for quote_char in ["'", '"']:
116
+ with pytest.raises(UnparseableVariableExpressionError):
117
+ parse_time_delta(quote_char + unquoted_raw)
118
+ with pytest.raises(UnparseableVariableExpressionError):
119
+ parse_time_delta(unquoted_raw + quote_char)
120
+
121
+ with pytest.raises(UnparseableVariableExpressionError):
122
+ parse_time_delta(VALID_QUOTE_CHARS[0] + quote_char + VALID_QUOTE_CHARS[1])
123
+ with pytest.raises(UnparseableVariableExpressionError):
124
+ parse_time_delta(VALID_QUOTE_CHARS[1] + quote_char + VALID_QUOTE_CHARS[0])
125
+
126
+ # Now do the actual test
127
+ if isinstance(expected, type):
128
+ for quote_char in VALID_QUOTE_CHARS:
129
+ with pytest.raises(expected):
130
+ parse_time_delta(quote_char + unquoted_raw + quote_char)
131
+ else:
132
+ for quote_char in VALID_QUOTE_CHARS:
133
+ actual = parse_time_delta(quote_char + unquoted_raw + quote_char)
134
+ assert isinstance(actual, timedelta)
135
+ assert actual == expected, f"Input string: {quote_char + unquoted_raw + quote_char}"
136
+
137
+
138
+ @pytest.mark.parametrize(
139
+ "var_body, expected",
140
+ [
141
+ ("1.23", Constant(1.23)),
142
+ ("123", Constant(123)),
143
+ (" 123 ", Constant(123)),
144
+ ("'-4.56 hours'", Constant(timedelta(hours=-4.56))),
145
+ ('"0.12 days"', Constant(timedelta(days=0.12))),
146
+ (" \t '-4.56 hours' \t ", Constant(timedelta(hours=-4.56))),
147
+ ("now", NamedVariable(NamedVariableType.NOW)),
148
+ ("NOW", UnparseableVariableExpressionError), # case sensitive
149
+ ("setMaxW", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W)),
150
+ ("SETMAXW", UnparseableVariableExpressionError), # case sensitive
151
+ ("foo", UnparseableVariableExpressionError), # unknown named variable
152
+ (
153
+ "0.5 * 0.2",
154
+ Expression(OperationType.MULTIPLY, Constant(0.5), Constant(0.2)),
155
+ ),
156
+ (
157
+ "0.5 * setMaxW",
158
+ Expression(OperationType.MULTIPLY, Constant(0.5), NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W)),
159
+ ),
160
+ (
161
+ "setMaxW / 2",
162
+ Expression(OperationType.DIVIDE, NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W), Constant(2)),
163
+ ),
164
+ (
165
+ " now - '-12 minutes' ",
166
+ Expression(OperationType.SUBTRACT, NamedVariable(NamedVariableType.NOW), Constant(timedelta(minutes=-12))),
167
+ ),
168
+ (
169
+ "now-'-12minutes'",
170
+ Expression(OperationType.SUBTRACT, NamedVariable(NamedVariableType.NOW), Constant(timedelta(minutes=-12))),
171
+ ),
172
+ (
173
+ 'now + "3 day"',
174
+ Expression(OperationType.ADD, NamedVariable(NamedVariableType.NOW), Constant(timedelta(days=3))),
175
+ ),
176
+ (
177
+ '"3 day" < "5 day"',
178
+ Expression(OperationType.LT, Constant(timedelta(days=3)), Constant(timedelta(days=5))),
179
+ ),
180
+ (
181
+ "rtgMaxW / 2",
182
+ Expression(OperationType.DIVIDE, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_W), Constant(2)),
183
+ ),
184
+ ("setMaxVA", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_VA)),
185
+ ("setMaxVar", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_VAR)),
186
+ ("setMaxChargeRateW", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_CHARGE_RATE_W)),
187
+ ("setMaxDischargeRateW", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_DISCHARGE_RATE_W)),
188
+ ("setMaxWh", NamedVariable(NamedVariableType.DERSETTING_SET_MAX_WH)),
189
+ (
190
+ "rtgMaxVar == 5.0",
191
+ Expression(OperationType.EQ, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_VAR), Constant(5.0)),
192
+ ),
193
+ (
194
+ "rtgMaxW != 0.5",
195
+ Expression(OperationType.NE, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_W), Constant(0.5)),
196
+ ),
197
+ (
198
+ "rtgMaxChargeRateW <= 0.5",
199
+ Expression(
200
+ OperationType.LTE, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_CHARGE_RATE_W), Constant(0.5)
201
+ ),
202
+ ),
203
+ (
204
+ "rtgMaxDischargeRateW > 0.5",
205
+ Expression(
206
+ OperationType.GT, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_DISCHARGE_RATE_W), Constant(0.5)
207
+ ),
208
+ ),
209
+ (
210
+ "rtgMaxWh >= 0.5",
211
+ Expression(OperationType.GTE, NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_WH), Constant(0.5)),
212
+ ),
213
+ ("now + foo", UnparseableVariableExpressionError),
214
+ ("now foo +", UnparseableVariableExpressionError),
215
+ ("now foo ", UnparseableVariableExpressionError),
216
+ ("7 + + 8 ", UnparseableVariableExpressionError),
217
+ ("'5 mins + now ", UnparseableVariableExpressionError), # Unterminated string literal
218
+ ("now + '5 mins", UnparseableVariableExpressionError), # Unterminated string literal
219
+ # Storage extension
220
+ (
221
+ "setMinWh >= 0.5",
222
+ Expression(OperationType.GTE, NamedVariable(NamedVariableType.DERSETTING_SET_MIN_WH), Constant(0.5)),
223
+ ),
224
+ (
225
+ "negRtgMaxChargeRateW + 50",
226
+ Expression(
227
+ OperationType.ADD,
228
+ NamedVariable(NamedVariableType.DERCAPABILITY_NEG_RTG_MAX_CHARGE_RATE_W),
229
+ Constant(50),
230
+ ),
231
+ ),
232
+ ],
233
+ )
234
+ def test_parse_variable_expression_body(
235
+ var_body: str, expected: type[Exception] | NamedVariable | Constant | Expression
236
+ ):
237
+ """Top level parsing test to ensure that a variety of variable bodies parse (or fail) in an expected fashion"""
238
+
239
+ if isinstance(expected, type):
240
+ with pytest.raises(expected):
241
+ parse_variable_expression_body(var_body, None)
242
+ else:
243
+ actual = parse_variable_expression_body(var_body, None)
244
+ assert isinstance(actual, NamedVariable) or isinstance(actual, Constant) or isinstance(actual, Expression)
245
+ assert actual == expected, f"Input string: {var_body}"
246
+
247
+
248
+ @pytest.mark.parametrize(
249
+ "var_body,expected,param_key",
250
+ [
251
+ (
252
+ "this < rtgMaxVA",
253
+ Expression(
254
+ OperationType.LT,
255
+ NamedVariable(NamedVariableType.DERSETTING_SET_MAX_VA),
256
+ NamedVariable(NamedVariableType.DERCAPABILITY_RTG_MAX_VA),
257
+ ),
258
+ "setMaxVA",
259
+ ),
260
+ (
261
+ "this < rtgMaxVA",
262
+ UnparseableVariableExpressionError,
263
+ "someUndefinedNamedVariable",
264
+ ),
265
+ ],
266
+ )
267
+ def test_parse_variable_expression_body_this(
268
+ var_body: str, expected: type[Exception] | NamedVariable | Constant | Expression, param_key: str | None
269
+ ) -> None:
270
+ """Parsing to ensure that variable bodies that contain `this` parse (or fail) as expected"""
271
+ if isinstance(expected, type):
272
+ with pytest.raises(expected):
273
+ parse_variable_expression_body(var_body, param_key)
274
+ else:
275
+ actual = parse_variable_expression_body(var_body, param_key)
276
+ assert isinstance(actual, NamedVariable) or isinstance(actual, Constant) or isinstance(actual, Expression)
277
+ assert actual == expected, f"Input string: {var_body}"
278
+
279
+
280
+ @pytest.mark.parametrize(
281
+ "input, expected",
282
+ [
283
+ (None, False),
284
+ ("", False),
285
+ ("string value", False),
286
+ (123, False),
287
+ (1.23, False),
288
+ (Decimal("1.2"), False),
289
+ (datetime(2022, 11, 3), False),
290
+ (timedelta(2), False),
291
+ (Action("", {}), False),
292
+ ([], False),
293
+ ({}, False),
294
+ (NamedVariable(NamedVariableType.NOW), True),
295
+ (NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W), True),
296
+ (Constant(1.23), True),
297
+ (Constant(timedelta(5)), True),
298
+ (Expression(OperationType.ADD, Constant(1.23), NamedVariable(NamedVariableType.NOW)), True),
299
+ ],
300
+ )
301
+ def test_is_resolvable_variable(input: Any, expected: bool):
302
+ result = is_resolvable_variable(input)
303
+ assert isinstance(result, bool)
304
+ assert result == expected
305
+
306
+
307
+ @pytest.mark.parametrize(
308
+ "parameter,variable,expected",
309
+ [
310
+ (Constant(value=0), NamedVariableType.DERSETTING_SET_MAX_W, False),
311
+ (Constant(value=5), NamedVariableType.DERSETTING_SET_MAX_W, False),
312
+ (NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W), NamedVariableType.DERSETTING_SET_MAX_W, True),
313
+ (NamedVariable(NamedVariableType.DERSETTING_SET_MAX_VA), NamedVariableType.DERSETTING_SET_MAX_W, False),
314
+ (
315
+ Expression(OperationType.ADD, Constant(1.23), NamedVariable(NamedVariableType.NOW)),
316
+ NamedVariableType.DERSETTING_SET_MAX_W,
317
+ False,
318
+ ),
319
+ (
320
+ Expression(OperationType.ADD, NamedVariable(NamedVariableType.NOW), Constant(1.23)),
321
+ NamedVariableType.DERSETTING_SET_MAX_W,
322
+ False,
323
+ ),
324
+ (
325
+ Expression(OperationType.ADD, Constant(1.23), NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W)),
326
+ NamedVariableType.DERSETTING_SET_MAX_W,
327
+ True,
328
+ ),
329
+ (
330
+ Expression(OperationType.ADD, NamedVariable(NamedVariableType.DERSETTING_SET_MAX_W), Constant(1.23)),
331
+ NamedVariableType.DERSETTING_SET_MAX_W,
332
+ True,
333
+ ),
334
+ ],
335
+ )
336
+ def test_has_named_variable(
337
+ parameter: NamedVariable | Expression | Constant, variable: NamedVariableType, expected: bool
338
+ ):
339
+ result = has_named_variable(parameter_value=parameter, named_variable=variable)
340
+ assert isinstance(result, bool)
341
+ assert result == expected
342
+
343
+
344
+ @pytest.mark.parametrize(
345
+ "input,expected",
346
+ [
347
+ ("SET_MAX_W", "setMaxW"),
348
+ ("SET_MAX_VAR", "setMaxVar"),
349
+ ("NOW", "now"),
350
+ ],
351
+ )
352
+ def test_snake_to_camel(input: str, expected: str) -> None:
353
+ assert snake_to_camel(input) == expected
354
+
355
+
356
+ @pytest.mark.parametrize(
357
+ "input,expected",
358
+ [
359
+ (NamedVariableType.DERCAPABILITY_RTG_MAX_VAR, "DERCapability.rtgMaxVar"),
360
+ (NamedVariableType.DERSETTING_SET_MIN_WH, "DERSetting.setMinWh"),
361
+ (NamedVariableType.DERCAPABILITY_RTG_MAX_VA, "DERCapability.rtgMaxVA"),
362
+ (NamedVariableType.DERSETTING_SET_MAX_VA, "DERSetting.setMaxVA"),
363
+ (NamedVariableType.DERCAPABILITY_NEG_RTG_MAX_CHARGE_RATE_W, "(-DERCapability.rtgMaxChargeRateW)"),
364
+ (NamedVariableType.NOW, "now"),
365
+ ],
366
+ )
367
+ def test_named_variable_repr(input: NamedVariableType, expected: str) -> None:
368
+ assert named_variable_repr(input) == expected
369
+
370
+
371
+ @pytest.mark.parametrize(
372
+ "input,expected",
373
+ [
374
+ (OperationType.ADD, "+"),
375
+ (OperationType.MULTIPLY, "*"),
376
+ (OperationType.SUBTRACT, "-"),
377
+ (OperationType.DIVIDE, "/"),
378
+ (OperationType.EQ, "=="),
379
+ (OperationType.GT, ">"),
380
+ (OperationType.GTE, ">="),
381
+ (OperationType.LT, "<"),
382
+ (OperationType.LTE, "<="),
383
+ (OperationType.NE, "!="),
384
+ ],
385
+ )
386
+ def test_operation_repr(input: OperationType, expected: str) -> None:
387
+ assert operation_repr(input) == expected
388
+
389
+
390
+ @pytest.mark.parametrize(
391
+ "input,expected",
392
+ [
393
+ (NamedVariable(variable=NamedVariableType.DERCAPABILITY_RTG_MAX_VA), "DERCapability.rtgMaxVA"),
394
+ (
395
+ Expression(OperationType.ADD, NamedVariable(NamedVariableType.DERSETTING_SET_MIN_WH), Constant(5.5)),
396
+ "DERSetting.setMinWh + 5.5",
397
+ ),
398
+ (Constant(654.456), "654.456"),
399
+ ],
400
+ )
401
+ def test_base_expression_expression_representation(input: BaseExpression, expected: str) -> None:
402
+ assert input.expression_representation() == expected