nextmv 0.36.0__tar.gz → 0.36.1.dev0__tar.gz
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.
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/PKG-INFO +1 -1
- nextmv-0.36.1.dev0/nextmv/__about__.py +1 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/batch_experiment.py +1 -1
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/instance.py +28 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/package.py +5 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/executor.py +2 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/manifest.py +2 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/run.py +25 -2
- nextmv-0.36.1.dev0/tests/cloud/test_instance.py +87 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_run.py +65 -0
- nextmv-0.36.0/nextmv/__about__.py +0 -1
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/.gitignore +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/LICENSE +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/README.md +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/__entrypoint__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/_serialization.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/base_model.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/account.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/application.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/client.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/input_set.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/scenario.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/secrets.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/url.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/cloud/version.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/.gitignore +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/README.md +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/app.yaml +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/input.json +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/main.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/deprecated.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/input.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/application.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/local.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/local/runner.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/logger.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/model.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/options.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/output.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/polling.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/safe.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/nextmv/status.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/pyproject.toml +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/cloud/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/cloud/app.yaml +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/cloud/test_client.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/cloud/test_package.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/cloud/test_scenario.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/local/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/local/test_application.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/local/test_executor.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/local/test_runner.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options1.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options2.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options3.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options4.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options5.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options6.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options7.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/scripts/options_deprecated.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_base_model.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_input.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_inputs/test_data.json +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_logger.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_manifest.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_model.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_options.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_output.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_polling.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_safe.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_serialization.py +0 -0
- {nextmv-0.36.0 → nextmv-0.36.1.dev0}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.36.1-dev.0"
|
|
@@ -257,7 +257,7 @@ class BatchExperimentRun(BaseModel):
|
|
|
257
257
|
repetition: int | None = None
|
|
258
258
|
"""Repetition number of the experiment."""
|
|
259
259
|
|
|
260
|
-
def
|
|
260
|
+
def model_post_init(self, __context) -> None:
|
|
261
261
|
"""
|
|
262
262
|
Logic to run after the class is initialized.
|
|
263
263
|
|
|
@@ -15,6 +15,7 @@ Instance
|
|
|
15
15
|
from datetime import datetime
|
|
16
16
|
|
|
17
17
|
from nextmv.base_model import BaseModel
|
|
18
|
+
from nextmv.run import RunQueuing
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class InstanceConfiguration(BaseModel):
|
|
@@ -37,6 +38,10 @@ class InstanceConfiguration(BaseModel):
|
|
|
37
38
|
Runtime options/parameters for the application.
|
|
38
39
|
secrets_collection_id : str, optional
|
|
39
40
|
ID of the secrets collection to use with this instance.
|
|
41
|
+
queuing : RunQueuing, optional
|
|
42
|
+
Queuing configuration for the instance.
|
|
43
|
+
integration_id : str, optional
|
|
44
|
+
ID of the integration to use for the instance.
|
|
40
45
|
|
|
41
46
|
Examples
|
|
42
47
|
--------
|
|
@@ -53,6 +58,29 @@ class InstanceConfiguration(BaseModel):
|
|
|
53
58
|
"""Options of the app that the instance uses."""
|
|
54
59
|
secrets_collection_id: str | None = None
|
|
55
60
|
"""ID of the secrets collection that the instance uses."""
|
|
61
|
+
queuing: RunQueuing | None = None
|
|
62
|
+
"""Queuing configuration for the instance."""
|
|
63
|
+
integration_id: str | None = None
|
|
64
|
+
"""ID of the integration to use for the instance."""
|
|
65
|
+
|
|
66
|
+
def model_post_init(self, __context) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Validations done after parsing the model.
|
|
69
|
+
|
|
70
|
+
Raises
|
|
71
|
+
------
|
|
72
|
+
ValueError
|
|
73
|
+
If execution_class is an empty string.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
if self.integration_id is None or self.integration_id == "":
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
integration_val = "integration"
|
|
80
|
+
if self.execution_class is not None and self.execution_class != "" and self.execution_class != integration_val:
|
|
81
|
+
raise ValueError(f"When integration_id is set, execution_class must be `{integration_val}` or None.")
|
|
82
|
+
|
|
83
|
+
self.execution_class = integration_val
|
|
56
84
|
|
|
57
85
|
|
|
58
86
|
class Instance(BaseModel):
|
|
@@ -16,6 +16,7 @@ from nextmv.model import Model, ModelConfiguration, _cleanup_python_model
|
|
|
16
16
|
_MANDATORY_FILES_PER_TYPE = {
|
|
17
17
|
ManifestType.PYTHON: ["main.py"],
|
|
18
18
|
ManifestType.GO: ["main"],
|
|
19
|
+
ManifestType.BINARY: ["main"],
|
|
19
20
|
ManifestType.JAVA: ["main.jar"],
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -277,7 +278,9 @@ def __install_dependencies( # noqa: C901 # complexity
|
|
|
277
278
|
"--platform=manylinux2014_aarch64",
|
|
278
279
|
"--platform=manylinux_2_17_aarch64",
|
|
279
280
|
"--platform=manylinux_2_24_aarch64",
|
|
281
|
+
"--platform=manylinux_2_26_aarch64",
|
|
280
282
|
"--platform=manylinux_2_28_aarch64",
|
|
283
|
+
"--platform=manylinux_2_34_aarch64",
|
|
281
284
|
"--platform=linux_aarch64",
|
|
282
285
|
]
|
|
283
286
|
)
|
|
@@ -287,7 +290,9 @@ def __install_dependencies( # noqa: C901 # complexity
|
|
|
287
290
|
"--platform=manylinux2014_x86_64",
|
|
288
291
|
"--platform=manylinux_2_17_x86_64",
|
|
289
292
|
"--platform=manylinux_2_24_x86_64",
|
|
293
|
+
"--platform=manylinux_2_26_x86_64",
|
|
290
294
|
"--platform=manylinux_2_28_x86_64",
|
|
295
|
+
"--platform=manylinux_2_34_x86_64",
|
|
291
296
|
"--platform=linux_x86_64",
|
|
292
297
|
]
|
|
293
298
|
)
|
|
@@ -1017,6 +1017,8 @@ def __determine_entrypoint(manifest: Manifest) -> str:
|
|
|
1017
1017
|
return "./main.py"
|
|
1018
1018
|
elif manifest.type == ManifestType.GO:
|
|
1019
1019
|
return "./main"
|
|
1020
|
+
elif manifest.type == ManifestType.BINARY:
|
|
1021
|
+
return "./main"
|
|
1020
1022
|
elif manifest.type == ManifestType.JAVA:
|
|
1021
1023
|
return "./main.jar"
|
|
1022
1024
|
else:
|
|
@@ -1081,7 +1081,7 @@ class RunQueuing(BaseModel):
|
|
|
1081
1081
|
queued. If False, the run will be queued.
|
|
1082
1082
|
"""
|
|
1083
1083
|
|
|
1084
|
-
def
|
|
1084
|
+
def model_post_init(self, __context) -> None:
|
|
1085
1085
|
"""
|
|
1086
1086
|
Validations done after parsing the model.
|
|
1087
1087
|
|
|
@@ -1121,6 +1121,8 @@ class RunConfiguration(BaseModel):
|
|
|
1121
1121
|
ID of the secrets collection to use for the run. Defaults to None.
|
|
1122
1122
|
queuing : RunQueuing, optional
|
|
1123
1123
|
Queuing configuration for the run. Defaults to None.
|
|
1124
|
+
integration_id : str, optional
|
|
1125
|
+
ID of the integration to use for the run. Defaults to None.
|
|
1124
1126
|
|
|
1125
1127
|
Examples
|
|
1126
1128
|
--------
|
|
@@ -1150,6 +1152,27 @@ class RunConfiguration(BaseModel):
|
|
|
1150
1152
|
"""ID of the secrets collection to use for the run."""
|
|
1151
1153
|
queuing: RunQueuing | None = None
|
|
1152
1154
|
"""Queuing configuration for the run."""
|
|
1155
|
+
integration_id: str | None = None
|
|
1156
|
+
"""ID of the integration to use for the run."""
|
|
1157
|
+
|
|
1158
|
+
def model_post_init(self, __context) -> None:
|
|
1159
|
+
"""
|
|
1160
|
+
Validations done after parsing the model.
|
|
1161
|
+
|
|
1162
|
+
Raises
|
|
1163
|
+
------
|
|
1164
|
+
ValueError
|
|
1165
|
+
If execution_class is an empty string.
|
|
1166
|
+
"""
|
|
1167
|
+
|
|
1168
|
+
if self.integration_id is None or self.integration_id == "":
|
|
1169
|
+
return
|
|
1170
|
+
|
|
1171
|
+
integration_val = "integration"
|
|
1172
|
+
if self.execution_class is not None and self.execution_class != "" and self.execution_class != integration_val:
|
|
1173
|
+
raise ValueError(f"When integration_id is set, execution_class must be `{integration_val}` or None.")
|
|
1174
|
+
|
|
1175
|
+
self.execution_class = integration_val
|
|
1153
1176
|
|
|
1154
1177
|
def resolve(
|
|
1155
1178
|
self,
|
|
@@ -1292,7 +1315,7 @@ class ExternalRunResult(BaseModel):
|
|
|
1292
1315
|
or `MULTI_FILE` output formats.
|
|
1293
1316
|
"""
|
|
1294
1317
|
|
|
1295
|
-
def
|
|
1318
|
+
def model_post_init(self, __context) -> None:
|
|
1296
1319
|
"""
|
|
1297
1320
|
Validations done after parsing the model.
|
|
1298
1321
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from nextmv.cloud.instance import InstanceConfiguration
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestInstanceConfigurationValidation(unittest.TestCase):
|
|
7
|
+
"""Test validation logic in InstanceConfiguration.model_post_init."""
|
|
8
|
+
|
|
9
|
+
def test_no_integration_id_no_validation(self):
|
|
10
|
+
"""Test that validation is skipped when integration_id is None or empty."""
|
|
11
|
+
# With None integration_id
|
|
12
|
+
config = InstanceConfiguration(integration_id=None, execution_class="small")
|
|
13
|
+
self.assertEqual(config.execution_class, "small")
|
|
14
|
+
|
|
15
|
+
# With empty string integration_id
|
|
16
|
+
config = InstanceConfiguration(integration_id="", execution_class="large")
|
|
17
|
+
self.assertEqual(config.execution_class, "large")
|
|
18
|
+
|
|
19
|
+
# With no integration_id specified
|
|
20
|
+
config = InstanceConfiguration(execution_class="medium")
|
|
21
|
+
self.assertEqual(config.execution_class, "medium")
|
|
22
|
+
|
|
23
|
+
def test_integration_id_with_integration_execution_class(self):
|
|
24
|
+
"""Test that integration_id with execution_class='integration' is valid."""
|
|
25
|
+
config = InstanceConfiguration(integration_id="int-12345", execution_class="integration")
|
|
26
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
27
|
+
self.assertEqual(config.execution_class, "integration")
|
|
28
|
+
|
|
29
|
+
def test_integration_id_with_none_execution_class(self):
|
|
30
|
+
"""Test that integration_id with execution_class=None sets it to 'integration'."""
|
|
31
|
+
config = InstanceConfiguration(integration_id="int-12345", execution_class=None)
|
|
32
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
33
|
+
self.assertEqual(config.execution_class, "integration")
|
|
34
|
+
|
|
35
|
+
def test_integration_id_without_execution_class(self):
|
|
36
|
+
"""Test that integration_id without execution_class sets it to 'integration'."""
|
|
37
|
+
config = InstanceConfiguration(integration_id="int-12345")
|
|
38
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
39
|
+
self.assertEqual(config.execution_class, "integration")
|
|
40
|
+
|
|
41
|
+
def test_integration_id_with_empty_execution_class(self):
|
|
42
|
+
"""Test that integration_id with execution_class='' sets it to 'integration'."""
|
|
43
|
+
config = InstanceConfiguration(integration_id="int-12345", execution_class="")
|
|
44
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
45
|
+
self.assertEqual(config.execution_class, "integration")
|
|
46
|
+
|
|
47
|
+
def test_integration_id_with_invalid_execution_class_raises_error(self):
|
|
48
|
+
"""Test that integration_id with non-integration execution_class raises ValueError."""
|
|
49
|
+
invalid_classes = ["small", "medium", "large", "custom", "standard"]
|
|
50
|
+
|
|
51
|
+
for execution_class in invalid_classes:
|
|
52
|
+
with self.subTest(execution_class=execution_class):
|
|
53
|
+
with self.assertRaises(ValueError) as context:
|
|
54
|
+
InstanceConfiguration(integration_id="int-12345", execution_class=execution_class)
|
|
55
|
+
|
|
56
|
+
error_msg = str(context.exception)
|
|
57
|
+
self.assertIn("When integration_id is set", error_msg)
|
|
58
|
+
self.assertIn("execution_class must be `integration` or None", error_msg)
|
|
59
|
+
|
|
60
|
+
def test_integration_id_error_message_format(self):
|
|
61
|
+
"""Test that the error message contains the expected format."""
|
|
62
|
+
with self.assertRaises(ValueError) as context:
|
|
63
|
+
InstanceConfiguration(integration_id="int-12345", execution_class="custom")
|
|
64
|
+
|
|
65
|
+
error_msg = str(context.exception)
|
|
66
|
+
# When using model_post_init, Pydantic wraps the error message
|
|
67
|
+
self.assertIn("When integration_id is set, execution_class must be `integration` or None.", error_msg)
|
|
68
|
+
|
|
69
|
+
def test_integration_id_with_other_configuration_options(self):
|
|
70
|
+
"""Test that integration_id works correctly with other configuration options."""
|
|
71
|
+
config = InstanceConfiguration(
|
|
72
|
+
integration_id="int-12345", options={"max_runtime": 30}, secrets_collection_id="sc_1234567890"
|
|
73
|
+
)
|
|
74
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
75
|
+
self.assertEqual(config.execution_class, "integration")
|
|
76
|
+
self.assertEqual(config.options, {"max_runtime": 30})
|
|
77
|
+
self.assertEqual(config.secrets_collection_id, "sc_1234567890")
|
|
78
|
+
|
|
79
|
+
def test_no_integration_id_with_other_options(self):
|
|
80
|
+
"""Test configuration without integration_id but with other options."""
|
|
81
|
+
config = InstanceConfiguration(
|
|
82
|
+
execution_class="small", options={"max_runtime": 60}, secrets_collection_id="sc_9876543210"
|
|
83
|
+
)
|
|
84
|
+
self.assertEqual(config.execution_class, "small")
|
|
85
|
+
self.assertEqual(config.options, {"max_runtime": 60})
|
|
86
|
+
self.assertEqual(config.secrets_collection_id, "sc_9876543210")
|
|
87
|
+
self.assertIsNone(config.integration_id)
|
|
@@ -8,6 +8,7 @@ from nextmv.run import (
|
|
|
8
8
|
FormatOutput,
|
|
9
9
|
Metadata,
|
|
10
10
|
Run,
|
|
11
|
+
RunConfiguration,
|
|
11
12
|
RunInformation,
|
|
12
13
|
RunTypeConfiguration,
|
|
13
14
|
run_duration,
|
|
@@ -206,3 +207,67 @@ class TestRunInformationToRun(unittest.TestCase):
|
|
|
206
207
|
|
|
207
208
|
run = run_info.to_run()
|
|
208
209
|
self.assertEqual(run.status_v2, status_v2)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TestRunConfigurationValidation(unittest.TestCase):
|
|
213
|
+
"""Test validation logic in RunConfiguration.model_post_init."""
|
|
214
|
+
|
|
215
|
+
def test_no_integration_id_no_validation(self):
|
|
216
|
+
"""Test that validation is skipped when integration_id is None or empty."""
|
|
217
|
+
# With None integration_id
|
|
218
|
+
config = RunConfiguration(integration_id=None, execution_class="small")
|
|
219
|
+
self.assertEqual(config.execution_class, "small")
|
|
220
|
+
|
|
221
|
+
# With empty string integration_id
|
|
222
|
+
config = RunConfiguration(integration_id="", execution_class="large")
|
|
223
|
+
self.assertEqual(config.execution_class, "large")
|
|
224
|
+
|
|
225
|
+
# With no integration_id specified
|
|
226
|
+
config = RunConfiguration(execution_class="medium")
|
|
227
|
+
self.assertEqual(config.execution_class, "medium")
|
|
228
|
+
|
|
229
|
+
def test_integration_id_with_integration_execution_class(self):
|
|
230
|
+
"""Test that integration_id with execution_class='integration' is valid."""
|
|
231
|
+
config = RunConfiguration(integration_id="int-12345", execution_class="integration")
|
|
232
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
233
|
+
self.assertEqual(config.execution_class, "integration")
|
|
234
|
+
|
|
235
|
+
def test_integration_id_with_none_execution_class(self):
|
|
236
|
+
"""Test that integration_id with execution_class=None sets it to 'integration'."""
|
|
237
|
+
config = RunConfiguration(integration_id="int-12345", execution_class=None)
|
|
238
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
239
|
+
self.assertEqual(config.execution_class, "integration")
|
|
240
|
+
|
|
241
|
+
def test_integration_id_without_execution_class(self):
|
|
242
|
+
"""Test that integration_id without execution_class sets it to 'integration'."""
|
|
243
|
+
config = RunConfiguration(integration_id="int-12345")
|
|
244
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
245
|
+
self.assertEqual(config.execution_class, "integration")
|
|
246
|
+
|
|
247
|
+
def test_integration_id_with_empty_execution_class(self):
|
|
248
|
+
"""Test that integration_id with execution_class='' sets it to 'integration'."""
|
|
249
|
+
config = RunConfiguration(integration_id="int-12345", execution_class="")
|
|
250
|
+
self.assertEqual(config.integration_id, "int-12345")
|
|
251
|
+
self.assertEqual(config.execution_class, "integration")
|
|
252
|
+
|
|
253
|
+
def test_integration_id_with_invalid_execution_class_raises_error(self):
|
|
254
|
+
"""Test that integration_id with non-integration execution_class raises ValueError."""
|
|
255
|
+
invalid_classes = ["small", "medium", "large", "custom", "standard"]
|
|
256
|
+
|
|
257
|
+
for execution_class in invalid_classes:
|
|
258
|
+
with self.subTest(execution_class=execution_class):
|
|
259
|
+
with self.assertRaises(ValueError) as context:
|
|
260
|
+
RunConfiguration(integration_id="int-12345", execution_class=execution_class)
|
|
261
|
+
|
|
262
|
+
error_msg = str(context.exception)
|
|
263
|
+
self.assertIn("When integration_id is set", error_msg)
|
|
264
|
+
self.assertIn("execution_class must be `integration` or None", error_msg)
|
|
265
|
+
|
|
266
|
+
def test_integration_id_error_message_format(self):
|
|
267
|
+
"""Test that the error message contains the expected format."""
|
|
268
|
+
with self.assertRaises(ValueError) as context:
|
|
269
|
+
RunConfiguration(integration_id="int-12345", execution_class="custom")
|
|
270
|
+
|
|
271
|
+
error_msg = str(context.exception)
|
|
272
|
+
# When using model_post_init, Pydantic wraps the error message
|
|
273
|
+
self.assertIn("When integration_id is set, execution_class must be `integration` or None.", error_msg)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v0.36.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|