Skip to content

Commit d194b8b

Browse files
committed
Add basic tests for long timers
1 parent 4028afd commit d194b8b

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

azure/durable_functions/models/DurableOrchestrationContext.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ def __init__(self,
5858
self._instance_id: str = instanceId
5959
self._is_replaying: bool = isReplaying
6060
self._parent_instance_id: str = parentInstanceId
61-
self._maximum_short_timer_duration: datetime.timedelta
61+
self._maximum_short_timer_duration: datetime.timedelta = None
6262
if maximumShortTimerDuration is not None:
6363
max_short_duration = parse_timespan_attrib(maximumShortTimerDuration)
6464
self._maximum_short_timer_duration = max_short_duration
65-
self._long_timer_interval_duration: datetime.timedelta
65+
self._long_timer_interval_duration: datetime.timedelta = None
6666
if longRunningTimerIntervalDuration is not None:
6767
long_interval_duration = parse_timespan_attrib(longRunningTimerIntervalDuration)
6868
self._long_timer_interval_duration = long_interval_duration

tests/tasks/test_long_timers.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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

Comments
 (0)