From 27447e533c943f0e1365bae5586a68b8803e0134 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Tue, 6 Jun 2023 21:25:26 -0400 Subject: [PATCH] Fix issue when timer start event is greater than a day (#303) * Fix issue when start time is greater than a day * Add some cycle timer tests * Add some more tests * Getting ./bin/pyl to pass --- .../specs/start_event.py | 8 +- .../unit/test_workflow_service.py | 167 ++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/specs/start_event.py b/spiffworkflow-backend/src/spiffworkflow_backend/specs/start_event.py index 5cb929e4..dd8fb89a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/specs/start_event.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/specs/start_event.py @@ -1,4 +1,5 @@ from datetime import datetime +from datetime import timedelta from typing import Any from SpiffWorkflow.bpmn.parser.util import full_tag # type: ignore @@ -39,24 +40,23 @@ class StartEvent(DefaultStartEvent): # type: ignore def configuration(self, my_task: SpiffTask, now_in_utc: datetime) -> StartConfiguration: evaluated_expression = self.evaluated_timer_expression(my_task) cycles = 0 - start_delay_in_seconds = 0 duration = 0 + time_delta = timedelta(seconds=0) if evaluated_expression is not None: if isinstance(self.timer_definition, TimeDateEventDefinition): parsed_duration = TimerEventDefinition.parse_time_or_duration(evaluated_expression) time_delta = parsed_duration - now_in_utc - start_delay_in_seconds = time_delta.seconds elif isinstance(self.timer_definition, DurationTimerEventDefinition): parsed_duration = TimerEventDefinition.parse_iso_duration(evaluated_expression) time_delta = TimerEventDefinition.get_timedelta_from_start(parsed_duration, now_in_utc) - start_delay_in_seconds = time_delta.seconds elif isinstance(self.timer_definition, CycleTimerEventDefinition): cycles, start, cycle_duration = TimerEventDefinition.parse_iso_recurring_interval(evaluated_expression) time_delta = start - now_in_utc + cycle_duration - start_delay_in_seconds = time_delta.seconds duration = cycle_duration.seconds + start_delay_in_seconds = int(time_delta.total_seconds()) + return (cycles, start_delay_in_seconds, duration) def evaluated_timer_expression(self, my_task: SpiffTask) -> Any: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_workflow_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_workflow_service.py index 191c8973..7f925090 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_workflow_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_workflow_service.py @@ -46,6 +46,14 @@ def example_start_datetime_minus_5_mins_in_utc( yield example_datetime - timedelta(minutes=5) +@pytest.fixture() +def example_start_datetime_minus_1_day_and_5_mins_in_utc( + example_start_datetime_in_utc_str: str, +) -> Generator[datetime, None, None]: + example_datetime = datetime.fromisoformat(example_start_datetime_in_utc_str) + yield example_datetime - timedelta(days=1, minutes=5) + + class CustomBpmnDmnParser(BpmnDmnParser): # type: ignore OVERRIDE_PARSER_CLASSES = {} OVERRIDE_PARSER_CLASSES.update(BpmnDmnParser.OVERRIDE_PARSER_CLASSES) @@ -85,6 +93,25 @@ class TestWorkflowService(BaseTest): _, delay, _ = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore assert delay == 0 + def regular_start_events_have_no_cycles(self, now_in_utc: datetime) -> None: + workflow = workflow_from_fragment( + """ + + + Flow_184umot + + + Flow_184umot + + + + """, + "no_tasks", + ) + cycles, _, duration = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore + assert cycles == 0 + assert duration == 0 + def test_run_at_delay_is_30_for_30_second_duration_start_timer_event(self, now_in_utc: datetime) -> None: workflow = workflow_from_fragment( """ @@ -106,6 +133,49 @@ class TestWorkflowService(BaseTest): _, delay, _ = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore assert delay == 30 + def test_run_at_delay_is_10000_for_10000_second_duration_start_timer_event(self, now_in_utc: datetime) -> None: + workflow = workflow_from_fragment( + """ + + + Flow_1x1o335 + + "PT10000S" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + _, delay, _ = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore + assert delay == 10000 + + def test_duration_start_timer_event_have_no_cylces(self, now_in_utc: datetime) -> None: + workflow = workflow_from_fragment( + """ + + + Flow_1x1o335 + + "PT30S" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + cycles, _, duration = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore + assert cycles == 0 + assert duration == 0 + def test_run_at_delay_is_300_if_5_mins_before_date_start_timer_event( self, example_start_datetime_in_utc_str: str, example_start_datetime_minus_5_mins_in_utc: datetime ) -> None: @@ -130,3 +200,100 @@ class TestWorkflowService(BaseTest): workflow, example_start_datetime_minus_5_mins_in_utc ) # type: ignore assert delay == 300 + + def test_run_at_delay_is_86700_if_1_day_and_5_mins_before_date_start_timer_event( + self, example_start_datetime_in_utc_str: str, example_start_datetime_minus_1_day_and_5_mins_in_utc: datetime + ) -> None: + workflow = workflow_from_fragment( + f""" + + + Flow_1x1o335 + + "{example_start_datetime_in_utc_str}" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + _, delay, _ = WorkflowService.next_start_event_configuration( + workflow, example_start_datetime_minus_1_day_and_5_mins_in_utc + ) # type: ignore + assert delay == 86700 + + def date_start_timer_event_has_no_cycles( + self, example_start_datetime_in_utc_str: str, example_start_datetime_minus_1_day_and_5_mins_in_utc: datetime + ) -> None: + workflow = workflow_from_fragment( + f""" + + + Flow_1x1o335 + + "{example_start_datetime_in_utc_str}" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + cycles, _, duration = WorkflowService.next_start_event_configuration( + workflow, example_start_datetime_minus_1_day_and_5_mins_in_utc + ) # type: ignore + assert cycles == 0 + assert duration == 0 + + def test_5_cycles_of_30_second_cycle_start_timer_event(self, now_in_utc: datetime) -> None: + workflow = workflow_from_fragment( + """ + + + Flow_1x1o335 + + "R5/PT30S" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + cycles, delay, duration = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore + assert cycles == 5 + assert delay == 30 + assert duration == 30 + + def test_5_cycles_of_10000_second_cycle_start_timer_event(self, now_in_utc: datetime) -> None: + workflow = workflow_from_fragment( + """ + + + Flow_1x1o335 + + "R5/PT10000S" + + + + + Flow_1x1o335 + + + """, + "Process_aldvgey", + ) + cycles, delay, duration = WorkflowService.next_start_event_configuration(workflow, now_in_utc) # type: ignore + assert cycles == 5 + assert delay == 10000 + assert duration == 10000