|
6 | 6 | from hexa.core.test import TestCase |
7 | 7 | from hexa.pipelines.models import ( |
8 | 8 | Pipeline, |
| 9 | + PipelineDoesNotSupportParametersError, |
9 | 10 | PipelineRun, |
10 | 11 | PipelineRunState, |
11 | 12 | PipelineRunTrigger, |
@@ -322,3 +323,165 @@ def test_is_schedulable_false_when_pinned_version_unschedulable(self): |
322 | 323 | self.PIPELINE.scheduled_pipeline_version = None |
323 | 324 | self.PIPELINE.save() |
324 | 325 | unschedulable_version.delete() |
| 326 | + |
| 327 | + def _disabling_version(self, config): |
| 328 | + return PipelineVersion.objects.create( |
| 329 | + pipeline=self.PIPELINE, |
| 330 | + name="disabling", |
| 331 | + parameters=[ |
| 332 | + { |
| 333 | + "code": "run_report_only", |
| 334 | + "type": "bool", |
| 335 | + "required": False, |
| 336 | + "default": False, |
| 337 | + "disables": ["data_input"], |
| 338 | + }, |
| 339 | + {"code": "data_input", "type": "str", "required": True}, |
| 340 | + ], |
| 341 | + config=config, |
| 342 | + ) |
| 343 | + |
| 344 | + def test_schedulable_when_required_param_disabled_by_active_controller(self): |
| 345 | + # Controller stored as on → data_input is disabled → required check waived. |
| 346 | + version = self._disabling_version({"run_report_only": True}) |
| 347 | + self.assertTrue(version.is_schedulable) |
| 348 | + version.delete() |
| 349 | + |
| 350 | + def test_not_schedulable_when_controller_off_and_required_param_empty(self): |
| 351 | + # Controller off (default) → data_input active and required with no value → not schedulable. |
| 352 | + version = self._disabling_version({}) |
| 353 | + self.assertFalse(version.is_schedulable) |
| 354 | + version.delete() |
| 355 | + |
| 356 | + def test_schedulable_with_disable_when_false_controller_off(self): |
| 357 | + version = PipelineVersion.objects.create( |
| 358 | + pipeline=self.PIPELINE, |
| 359 | + name="enable-toggle", |
| 360 | + parameters=[ |
| 361 | + { |
| 362 | + "code": "enable_advanced", |
| 363 | + "type": "bool", |
| 364 | + "required": False, |
| 365 | + "default": False, |
| 366 | + "disables": ["tuning"], |
| 367 | + "disable_when": False, |
| 368 | + }, |
| 369 | + {"code": "tuning", "type": "str", "required": True}, |
| 370 | + ], |
| 371 | + config={}, |
| 372 | + ) |
| 373 | + # enable_advanced off (default) matches disable_when=False → tuning disabled → schedulable. |
| 374 | + self.assertTrue(version.is_schedulable) |
| 375 | + version.delete() |
| 376 | + |
| 377 | + def test_not_schedulable_with_disable_when_false_controller_on(self): |
| 378 | + version = PipelineVersion.objects.create( |
| 379 | + pipeline=self.PIPELINE, |
| 380 | + name="enable-toggle-on", |
| 381 | + parameters=[ |
| 382 | + { |
| 383 | + "code": "enable_advanced", |
| 384 | + "type": "bool", |
| 385 | + "required": False, |
| 386 | + "default": False, |
| 387 | + "disables": ["tuning"], |
| 388 | + "disable_when": False, |
| 389 | + }, |
| 390 | + {"code": "tuning", "type": "str", "required": True}, |
| 391 | + ], |
| 392 | + config={"enable_advanced": True}, |
| 393 | + ) |
| 394 | + # Controller on → does NOT match disable_when=False → tuning active, required, empty → not schedulable. |
| 395 | + self.assertFalse(version.is_schedulable) |
| 396 | + version.delete() |
| 397 | + |
| 398 | + def test_not_schedulable_when_controller_explicitly_false_in_config(self): |
| 399 | + # Explicit False in config (not just default) keeps the required param active. |
| 400 | + version = self._disabling_version({"run_report_only": False}) |
| 401 | + self.assertFalse(version.is_schedulable) |
| 402 | + version.delete() |
| 403 | + |
| 404 | + def test_schedulable_unions_multiple_controllers(self): |
| 405 | + version = PipelineVersion.objects.create( |
| 406 | + pipeline=self.PIPELINE, |
| 407 | + name="multi-controller", |
| 408 | + parameters=[ |
| 409 | + { |
| 410 | + "code": "toggle_a", |
| 411 | + "type": "bool", |
| 412 | + "required": False, |
| 413 | + "default": False, |
| 414 | + "disables": ["x_param"], |
| 415 | + }, |
| 416 | + { |
| 417 | + "code": "toggle_b", |
| 418 | + "type": "bool", |
| 419 | + "required": False, |
| 420 | + "default": False, |
| 421 | + "disables": ["x_param", "y_param"], |
| 422 | + }, |
| 423 | + {"code": "x_param", "type": "str", "required": True}, |
| 424 | + {"code": "y_param", "type": "str", "required": True}, |
| 425 | + ], |
| 426 | + config={"toggle_a": True, "toggle_b": True}, |
| 427 | + ) |
| 428 | + self.assertTrue(version.is_schedulable) |
| 429 | + version.delete() |
| 430 | + |
| 431 | + def test_schedulable_when_required_disabled_param_has_no_default(self): |
| 432 | + # Sanity: a disabled required param needs no default to be schedulable. |
| 433 | + version = self._disabling_version({"run_report_only": True}) |
| 434 | + data_input = next(p for p in version.parameters if p["code"] == "data_input") |
| 435 | + self.assertNotIn("default", data_input) |
| 436 | + self.assertTrue(version.is_schedulable) |
| 437 | + version.delete() |
| 438 | + |
| 439 | + def test_get_disabled_parameter_codes_handles_none_config(self): |
| 440 | + version = self._disabling_version({}) |
| 441 | + # Controller defaults to False → no params disabled. |
| 442 | + self.assertEqual(version.get_disabled_parameter_codes(None), set()) |
| 443 | + version.delete() |
| 444 | + |
| 445 | + def _scheduled_disabling_version(self, current_config): |
| 446 | + """A version that currently has `data_input` set and is disabled-capable.""" |
| 447 | + return PipelineVersion.objects.create( |
| 448 | + pipeline=self.PIPELINE, |
| 449 | + name="scheduled-disabling", |
| 450 | + parameters=[ |
| 451 | + { |
| 452 | + "code": "run_report_only", |
| 453 | + "type": "bool", |
| 454 | + "required": False, |
| 455 | + "default": False, |
| 456 | + "disables": ["data_input"], |
| 457 | + }, |
| 458 | + {"code": "data_input", "type": "str", "required": True}, |
| 459 | + ], |
| 460 | + config=current_config, |
| 461 | + ) |
| 462 | + |
| 463 | + def test_validate_new_config_allows_dropping_disabled_required_param(self): |
| 464 | + version = self._scheduled_disabling_version( |
| 465 | + {"run_report_only": False, "data_input": "real_value"} |
| 466 | + ) |
| 467 | + # New config turns the controller on → data_input disabled → dropping it is allowed. |
| 468 | + version.validate_new_config({"run_report_only": True, "data_input": None}) |
| 469 | + version.delete() |
| 470 | + |
| 471 | + def test_validate_new_config_rejects_dropping_active_required_param(self): |
| 472 | + version = self._scheduled_disabling_version( |
| 473 | + {"run_report_only": False, "data_input": "real_value"} |
| 474 | + ) |
| 475 | + # Controller stays off → data_input active and required → dropping its value must raise. |
| 476 | + with self.assertRaises(PipelineDoesNotSupportParametersError): |
| 477 | + version.validate_new_config({"run_report_only": False, "data_input": None}) |
| 478 | + version.delete() |
| 479 | + |
| 480 | + def test_validate_new_config_passes_when_value_kept(self): |
| 481 | + version = self._scheduled_disabling_version( |
| 482 | + {"run_report_only": False, "data_input": "real_value"} |
| 483 | + ) |
| 484 | + version.validate_new_config( |
| 485 | + {"run_report_only": False, "data_input": "still_here"} |
| 486 | + ) |
| 487 | + version.delete() |
0 commit comments