|
| 1 | +import datetime |
| 2 | + |
| 3 | +import pytest |
| 4 | +from azure.durable_functions.models.DurableOrchestrationContext import DurableOrchestrationContext |
| 5 | +from azure.durable_functions.models.Task import LongTimerTask, TaskState, TimerTask |
| 6 | +from azure.durable_functions.models.actions.CreateTimerAction import CreateTimerAction |
| 7 | + |
| 8 | + |
| 9 | +@pytest.fixture |
| 10 | +def starting_context_v3(): |
| 11 | + context = DurableOrchestrationContext.from_json( |
| 12 | + '{"history":[{"EventType":12,"EventId":-1,"IsPlayed":false,' |
| 13 | + '"Timestamp":"' |
| 14 | + f'{datetime.datetime.now(datetime.timezone.utc).isoformat()}' |
| 15 | + '"}, {"OrchestrationInstance":{' |
| 16 | + '"InstanceId":"48d0f95957504c2fa579e810a390b938", ' |
| 17 | + '"ExecutionId":"fd183ee02e4b4fd18c95b773cfb5452b"},"EventType":0,' |
| 18 | + '"ParentInstance":null, ' |
| 19 | + '"Name":"DurableOrchestratorTrigger","Version":"","Input":"null",' |
| 20 | + '"Tags":null,"EventId":-1,"IsPlayed":false, ' |
| 21 | + '"Timestamp":"' |
| 22 | + f'{datetime.datetime.now(datetime.timezone.utc).isoformat()}' |
| 23 | + '"}],"input":null,' |
| 24 | + '"instanceId":"48d0f95957504c2fa579e810a390b938", ' |
| 25 | + '"upperSchemaVersion": 2, ' |
| 26 | + '"upperSchemaVersionNew": 3, ' |
| 27 | + '"isReplaying":false,"parentInstanceId":null, ' |
| 28 | + '"maximumShortTimerDuration":"0.16:00:00", ' |
| 29 | + '"longRunningTimerIntervalDuration":"0.08:00:00" } ') |
| 30 | + return context |
| 31 | + |
| 32 | + |
| 33 | +def test_durable_context_creates_correct_timer(starting_context_v3): |
| 34 | + timer = starting_context_v3.create_timer(datetime.datetime.now(datetime.timezone.utc) + |
| 35 | + datetime.timedelta(minutes=30)) |
| 36 | + assert isinstance(timer, TimerTask) |
| 37 | + |
| 38 | + timer2 = starting_context_v3.create_timer(datetime.datetime.now(datetime.timezone.utc) + |
| 39 | + datetime.timedelta(days=1)) |
| 40 | + assert isinstance(timer2, LongTimerTask) |
| 41 | + |
| 42 | +def test_long_timer_fires_appropriately(starting_context_v3): |
| 43 | + starting_time = starting_context_v3.current_utc_datetime |
| 44 | + final_fire_time = starting_time + datetime.timedelta(hours=20) |
| 45 | + long_timer_action = CreateTimerAction(final_fire_time) |
| 46 | + long_timer_task = LongTimerTask(None, long_timer_action, starting_context_v3) |
| 47 | + assert long_timer_task.action.fire_at == final_fire_time |
| 48 | + assert long_timer_task.action == long_timer_action |
| 49 | + |
| 50 | + # Check the first "inner" timer and simulate firing it |
| 51 | + short_timer_task = long_timer_task.pending_tasks.pop() |
| 52 | + assert short_timer_task.action_repr.fire_at == starting_time + datetime.timedelta(hours=8) |
| 53 | + # This happens when the task is reconstructed during replay, doing it manually for the test |
| 54 | + long_timer_task.orchestration_context.current_utc_datetime = short_timer_task.action_repr.fire_at |
| 55 | + short_timer_task.state = TaskState.SUCCEEDED |
| 56 | + long_timer_task.try_set_value(short_timer_task) |
| 57 | + |
| 58 | + assert long_timer_task.state == TaskState.RUNNING |
| 59 | + |
| 60 | + # Check the scond "inner" timer and simulate firing it. This one should be set to the final |
| 61 | + # fire time, the remaining time (12 hours) is less than the max long timer duration (16 hours) |
| 62 | + short_timer_task = long_timer_task.pending_tasks.pop() |
| 63 | + assert short_timer_task.action_repr.fire_at == final_fire_time |
| 64 | + long_timer_task.orchestration_context.current_utc_datetime = short_timer_task.action_repr.fire_at |
| 65 | + short_timer_task.state = TaskState.SUCCEEDED |
| 66 | + long_timer_task.try_set_value(short_timer_task) |
| 67 | + |
| 68 | + # Ensure the LongTimerTask finished |
| 69 | + assert len(long_timer_task.pending_tasks) == 0 |
| 70 | + assert long_timer_task.state == TaskState.SUCCEEDED |
0 commit comments