moto/tests/test_swf/models/test_workflow_execution.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

693 lines
25 KiB
Python
Raw Normal View History

from threading import Timer as ThreadingTimer
from time import sleep
from freezegun import freeze_time
from unittest.mock import Mock, patch
2021-10-18 19:44:29 +00:00
import sure # noqa # pylint: disable=unused-import
from moto.swf.models import (
ActivityType,
Timeout,
Timer,
WorkflowType,
WorkflowExecution,
)
from moto.swf.exceptions import SWFDefaultUndefinedFault
from ..utils import (
auto_start_decision_tasks,
get_basic_domain,
get_basic_workflow_type,
make_workflow_execution,
)
2015-10-26 22:16:59 +00:00
VALID_ACTIVITY_TASK_ATTRIBUTES = {
"activityId": "my-activity-001",
2016-02-02 19:02:37 +00:00
"activityType": {"name": "test-activity", "version": "v1.1"},
"taskList": {"name": "task-list-name"},
2015-10-26 22:16:59 +00:00
"scheduleToStartTimeout": "600",
"scheduleToCloseTimeout": "600",
"startToCloseTimeout": "600",
"heartbeatTimeout": "300",
}
2016-02-02 19:02:37 +00:00
def test_workflow_execution_creation():
domain = get_basic_domain()
wft = get_basic_workflow_type()
wfe = WorkflowExecution(domain, wft, "ab1234", child_policy="TERMINATE")
wfe.domain.should.equal(domain)
wfe.workflow_type.should.equal(wft)
wfe.child_policy.should.equal("TERMINATE")
2016-02-02 19:02:37 +00:00
def test_workflow_execution_creation_child_policy_logic():
domain = get_basic_domain()
WorkflowExecution(
domain,
WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
),
"ab1234",
).child_policy.should.equal("ABANDON")
WorkflowExecution(
domain,
WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
),
"ab1234",
child_policy="REQUEST_CANCEL",
).child_policy.should.equal("REQUEST_CANCEL")
WorkflowExecution.when.called_with(
domain, WorkflowType("test-workflow", "v1.0"), "ab1234"
).should.throw(SWFDefaultUndefinedFault)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_string_representation():
wfe = make_workflow_execution(child_policy="TERMINATE")
str(wfe).should.match(r"^WorkflowExecution\(run_id: .*\)")
2016-02-02 19:02:37 +00:00
def test_workflow_execution_generates_a_random_run_id():
domain = get_basic_domain()
wft = get_basic_workflow_type()
wfe1 = WorkflowExecution(domain, wft, "ab1234", child_policy="TERMINATE")
wfe2 = WorkflowExecution(domain, wft, "ab1235", child_policy="TERMINATE")
wfe1.run_id.should_not.equal(wfe2.run_id)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_short_dict_representation():
domain = get_basic_domain()
wf_type = WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
)
wfe = WorkflowExecution(domain, wf_type, "ab1234")
sd = wfe.to_short_dict()
sd["workflowId"].should.equal("ab1234")
sd.should.contain("runId")
2016-02-02 19:02:37 +00:00
def test_workflow_execution_medium_dict_representation():
domain = get_basic_domain()
wf_type = WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
)
wfe = WorkflowExecution(domain, wf_type, "ab1234")
md = wfe.to_medium_dict()
md["execution"].should.equal(wfe.to_short_dict())
md["workflowType"].should.equal(wf_type.to_short_dict())
md["startTimestamp"].should.be.a("float")
md["executionStatus"].should.equal("OPEN")
md["cancelRequested"].should.equal(False)
md.should_not.contain("tagList")
wfe.tag_list = ["foo", "bar", "baz"]
md = wfe.to_medium_dict()
md["tagList"].should.equal(["foo", "bar", "baz"])
2016-02-02 19:02:37 +00:00
def test_workflow_execution_full_dict_representation():
domain = get_basic_domain()
wf_type = WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
)
wfe = WorkflowExecution(domain, wf_type, "ab1234")
fd = wfe.to_full_dict()
fd["executionInfo"].should.equal(wfe.to_medium_dict())
fd["openCounts"]["openTimers"].should.equal(0)
fd["openCounts"]["openDecisionTasks"].should.equal(0)
fd["openCounts"]["openActivityTasks"].should.equal(0)
fd["executionConfiguration"].should.equal(
{
"childPolicy": "ABANDON",
"executionStartToCloseTimeout": "300",
"taskList": {"name": "queue"},
"taskStartToCloseTimeout": "300",
}
)
def test_closed_workflow_execution_full_dict_representation():
domain = get_basic_domain()
wf_type = WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
)
wfe = WorkflowExecution(domain, wf_type, "ab1234")
wfe.execution_status = "CLOSED"
wfe.close_status = "CANCELED"
wfe.close_timestamp = 1420066801.123
fd = wfe.to_full_dict()
medium_dict = wfe.to_medium_dict()
medium_dict["closeStatus"] = "CANCELED"
medium_dict["closeTimestamp"] = 1420066801.123
fd["executionInfo"].should.equal(medium_dict)
fd["openCounts"]["openTimers"].should.equal(0)
fd["openCounts"]["openDecisionTasks"].should.equal(0)
fd["openCounts"]["openActivityTasks"].should.equal(0)
fd["executionConfiguration"].should.equal(
{
"childPolicy": "ABANDON",
"executionStartToCloseTimeout": "300",
"taskList": {"name": "queue"},
"taskStartToCloseTimeout": "300",
}
)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_list_dict_representation():
domain = get_basic_domain()
wf_type = WorkflowType(
"test-workflow",
"v1.0",
task_list="queue",
default_child_policy="ABANDON",
default_execution_start_to_close_timeout="300",
default_task_start_to_close_timeout="300",
)
wfe = WorkflowExecution(domain, wf_type, "ab1234")
ld = wfe.to_list_dict()
ld["workflowType"]["version"].should.equal("v1.0")
ld["workflowType"]["name"].should.equal("test-workflow")
ld["executionStatus"].should.equal("OPEN")
ld["execution"]["workflowId"].should.equal("ab1234")
ld["execution"].should.contain("runId")
ld["cancelRequested"].should.equal(False)
ld.should.contain("startTimestamp")
def test_workflow_execution_schedule_decision_task():
wfe = make_workflow_execution()
wfe.open_counts["openDecisionTasks"].should.equal(0)
wfe.schedule_decision_task()
wfe.open_counts["openDecisionTasks"].should.equal(1)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_dont_schedule_decision_if_existing_started_and_other_scheduled():
wfe = make_workflow_execution()
wfe.open_counts["openDecisionTasks"].should.equal(0)
wfe.schedule_decision_task()
wfe.open_counts["openDecisionTasks"].should.equal(1)
wfe.decision_tasks[0].start("evt_id")
wfe.schedule_decision_task()
wfe.schedule_decision_task()
wfe.open_counts["openDecisionTasks"].should.equal(2)
def test_workflow_execution_schedule_decision_if_existing_started_and_no_other_scheduled():
wfe = make_workflow_execution()
wfe.open_counts["openDecisionTasks"].should.equal(0)
wfe.schedule_decision_task()
wfe.open_counts["openDecisionTasks"].should.equal(1)
wfe.decision_tasks[0].start("evt_id")
wfe.schedule_decision_task()
wfe.open_counts["openDecisionTasks"].should.equal(2)
def test_workflow_execution_start_decision_task():
wfe = make_workflow_execution()
wfe.schedule_decision_task()
dt = wfe.decision_tasks[0]
wfe.start_decision_task(dt.task_token, identity="srv01")
dt = wfe.decision_tasks[0]
dt.state.should.equal("STARTED")
wfe.events()[-1].event_type.should.equal("DecisionTaskStarted")
wfe.events()[-1].event_attributes["identity"].should.equal("srv01")
2016-02-02 19:02:37 +00:00
def test_workflow_execution_history_events_ids():
wfe = make_workflow_execution()
wfe._add_event("WorkflowExecutionStarted")
wfe._add_event("DecisionTaskScheduled")
wfe._add_event("DecisionTaskStarted")
ids = [evt.event_id for evt in wfe.events()]
ids.should.equal([1, 2, 3])
2016-02-02 19:02:37 +00:00
@freeze_time("2015-01-01 12:00:00")
def test_workflow_execution_start():
wfe = make_workflow_execution()
wfe.events().should.equal([])
wfe.start()
wfe.start_timestamp.should.equal(1420113600.0)
wfe.events().should.have.length_of(2)
wfe.events()[0].event_type.should.equal("WorkflowExecutionStarted")
wfe.events()[1].event_type.should.equal("DecisionTaskScheduled")
2016-02-02 19:02:37 +00:00
@freeze_time("2015-01-02 12:00:00")
def test_workflow_execution_complete():
wfe = make_workflow_execution()
wfe.complete(123, result="foo")
wfe.execution_status.should.equal("CLOSED")
wfe.close_status.should.equal("COMPLETED")
wfe.close_timestamp.should.equal(1420200000.0)
wfe.events()[-1].event_type.should.equal("WorkflowExecutionCompleted")
wfe.events()[-1].event_attributes["decisionTaskCompletedEventId"].should.equal(123)
wfe.events()[-1].event_attributes["result"].should.equal("foo")
2016-02-02 19:02:37 +00:00
@freeze_time("2015-01-02 12:00:00")
def test_workflow_execution_fail():
wfe = make_workflow_execution()
wfe.fail(123, details="some details", reason="my rules")
wfe.execution_status.should.equal("CLOSED")
wfe.close_status.should.equal("FAILED")
wfe.close_timestamp.should.equal(1420200000.0)
wfe.events()[-1].event_type.should.equal("WorkflowExecutionFailed")
wfe.events()[-1].event_attributes["decisionTaskCompletedEventId"].should.equal(123)
wfe.events()[-1].event_attributes["details"].should.equal("some details")
wfe.events()[-1].event_attributes["reason"].should.equal("my rules")
2016-02-02 19:02:37 +00:00
@freeze_time("2015-01-01 12:00:00")
def test_workflow_execution_schedule_activity_task():
wfe = make_workflow_execution()
wfe.latest_activity_task_timestamp.should.equal(None)
2015-10-26 22:16:59 +00:00
wfe.schedule_activity_task(123, VALID_ACTIVITY_TASK_ATTRIBUTES)
wfe.latest_activity_task_timestamp.should.equal(1420113600.0)
wfe.open_counts["openActivityTasks"].should.equal(1)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ActivityTaskScheduled")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
last_event.event_attributes["taskList"]["name"].should.equal("task-list-name")
wfe.activity_tasks.should.have.length_of(1)
task = wfe.activity_tasks[0]
task.activity_id.should.equal("my-activity-001")
task.activity_type.name.should.equal("test-activity")
wfe.domain.activity_task_lists["task-list-name"].should.contain(task)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_schedule_activity_task_without_task_list_should_take_default():
wfe = make_workflow_execution()
wfe.domain.add_type(ActivityType("test-activity", "v1.2", task_list="foobar"))
wfe.schedule_activity_task(
2019-10-31 15:44:26 +00:00
123,
{
"activityId": "my-activity-001",
2016-02-02 19:02:37 +00:00
"activityType": {"name": "test-activity", "version": "v1.2"},
"scheduleToStartTimeout": "600",
"scheduleToCloseTimeout": "600",
"startToCloseTimeout": "600",
"heartbeatTimeout": "300",
2019-10-31 15:44:26 +00:00
},
)
wfe.open_counts["openActivityTasks"].should.equal(1)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ActivityTaskScheduled")
last_event.event_attributes["taskList"]["name"].should.equal("foobar")
task = wfe.activity_tasks[0]
wfe.domain.activity_task_lists["foobar"].should.contain(task)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_schedule_activity_task_should_fail_if_wrong_attributes():
wfe = make_workflow_execution()
at = ActivityType("test-activity", "v1.1")
at.status = "DEPRECATED"
wfe.domain.add_type(at)
wfe.domain.add_type(ActivityType("test-activity", "v1.2"))
hsh = {
"activityId": "my-activity-001",
2016-02-02 19:02:37 +00:00
"activityType": {"name": "test-activity-does-not-exists", "version": "v1.1"},
}
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal("ACTIVITY_TYPE_DOES_NOT_EXIST")
hsh["activityType"]["name"] = "test-activity"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal("ACTIVITY_TYPE_DEPRECATED")
hsh["activityType"]["version"] = "v1.2"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal("DEFAULT_TASK_LIST_UNDEFINED")
2016-02-02 19:02:37 +00:00
hsh["taskList"] = {"name": "foobar"}
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal(
"DEFAULT_SCHEDULE_TO_START_TIMEOUT_UNDEFINED"
)
hsh["scheduleToStartTimeout"] = "600"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal(
"DEFAULT_SCHEDULE_TO_CLOSE_TIMEOUT_UNDEFINED"
)
hsh["scheduleToCloseTimeout"] = "600"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal(
"DEFAULT_START_TO_CLOSE_TIMEOUT_UNDEFINED"
)
hsh["startToCloseTimeout"] = "600"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal(
"DEFAULT_HEARTBEAT_TIMEOUT_UNDEFINED"
)
wfe.open_counts["openActivityTasks"].should.equal(0)
wfe.activity_tasks.should.have.length_of(0)
wfe.domain.activity_task_lists.should.have.length_of(0)
hsh["heartbeatTimeout"] = "300"
wfe.schedule_activity_task(123, hsh)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ActivityTaskScheduled")
task = wfe.activity_tasks[0]
wfe.domain.activity_task_lists["foobar"].should.contain(task)
wfe.open_counts["openDecisionTasks"].should.equal(0)
wfe.open_counts["openActivityTasks"].should.equal(1)
2016-02-02 19:02:37 +00:00
def test_workflow_execution_schedule_activity_task_failure_triggers_new_decision():
wfe = make_workflow_execution()
wfe.start()
task_token = wfe.decision_tasks[-1].task_token
wfe.start_decision_task(task_token)
2016-02-02 19:02:37 +00:00
wfe.complete_decision_task(
task_token,
execution_context="free-form execution context",
decisions=[
{
"decisionType": "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes": {
"activityId": "my-activity-001",
"activityType": {
"name": "test-activity-does-not-exist",
"version": "v1.2",
},
},
},
{
"decisionType": "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes": {
"activityId": "my-activity-001",
"activityType": {
"name": "test-activity-does-not-exist",
"version": "v1.2",
},
},
},
],
)
wfe.latest_execution_context.should.equal("free-form execution context")
wfe.open_counts["openActivityTasks"].should.equal(0)
wfe.open_counts["openDecisionTasks"].should.equal(1)
last_events = wfe.events()[-3:]
last_events[0].event_type.should.equal("ScheduleActivityTaskFailed")
last_events[1].event_type.should.equal("ScheduleActivityTaskFailed")
last_events[2].event_type.should.equal("DecisionTaskScheduled")
2016-02-02 19:02:37 +00:00
def test_workflow_execution_schedule_activity_task_with_same_activity_id():
wfe = make_workflow_execution()
2015-10-26 22:16:59 +00:00
wfe.schedule_activity_task(123, VALID_ACTIVITY_TASK_ATTRIBUTES)
wfe.open_counts["openActivityTasks"].should.equal(1)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ActivityTaskScheduled")
2015-10-26 22:16:59 +00:00
wfe.schedule_activity_task(123, VALID_ACTIVITY_TASK_ATTRIBUTES)
wfe.open_counts["openActivityTasks"].should.equal(1)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
2017-02-24 02:37:43 +00:00
last_event.event_attributes["cause"].should.equal("ACTIVITY_ID_ALREADY_IN_USE")
2015-10-26 22:16:59 +00:00
2016-02-02 19:02:37 +00:00
2015-10-26 22:16:59 +00:00
def test_workflow_execution_start_activity_task():
wfe = make_workflow_execution()
wfe.schedule_activity_task(123, VALID_ACTIVITY_TASK_ATTRIBUTES)
task_token = wfe.activity_tasks[-1].task_token
wfe.start_activity_task(task_token, identity="worker01")
task = wfe.activity_tasks[-1]
task.state.should.equal("STARTED")
wfe.events()[-1].event_type.should.equal("ActivityTaskStarted")
wfe.events()[-1].event_attributes["identity"].should.equal("worker01")
2016-02-02 19:02:37 +00:00
def test_complete_activity_task():
wfe = make_workflow_execution()
wfe.schedule_activity_task(123, VALID_ACTIVITY_TASK_ATTRIBUTES)
task_token = wfe.activity_tasks[-1].task_token
wfe.open_counts["openActivityTasks"].should.equal(1)
wfe.open_counts["openDecisionTasks"].should.equal(0)
wfe.start_activity_task(task_token, identity="worker01")
wfe.complete_activity_task(task_token, result="a superb result")
task = wfe.activity_tasks[-1]
task.state.should.equal("COMPLETED")
wfe.events()[-2].event_type.should.equal("ActivityTaskCompleted")
wfe.events()[-1].event_type.should.equal("DecisionTaskScheduled")
wfe.open_counts["openActivityTasks"].should.equal(0)
wfe.open_counts["openDecisionTasks"].should.equal(1)
2016-02-02 19:02:37 +00:00
def test_terminate():
wfe = make_workflow_execution()
wfe.schedule_decision_task()
wfe.terminate()
wfe.execution_status.should.equal("CLOSED")
wfe.close_status.should.equal("TERMINATED")
wfe.close_cause.should.equal("OPERATOR_INITIATED")
wfe.open_counts["openDecisionTasks"].should.equal(1)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("WorkflowExecutionTerminated")
# take default child_policy if not provided (as here)
last_event.event_attributes["childPolicy"].should.equal("ABANDON")
2016-02-02 19:02:37 +00:00
def test_first_timeout():
wfe = make_workflow_execution()
wfe.first_timeout().should.be.none
with freeze_time("2015-01-01 12:00:00"):
wfe.start()
wfe.first_timeout().should.be.none
with freeze_time("2015-01-01 14:01"):
# 2 hours timeout reached
wfe.first_timeout().should.be.a(Timeout)
2016-02-02 19:02:37 +00:00
2017-02-24 02:37:43 +00:00
# See moto/swf/models/workflow_execution.py "_process_timeouts()" for more
# details
def test_timeouts_are_processed_in_order_and_reevaluated():
# Let's make a Workflow Execution with the following properties:
# - execution start to close timeout of 8 mins
# - (decision) task start to close timeout of 5 mins
#
# Now start the workflow execution, and look at the history 15 mins later:
# - a first decision task is fired just after workflow execution start
# - the first decision task should have timed out after 5 mins
# - that fires a new decision task (which we hack to start automatically)
# - then the workflow timeouts after 8 mins (shows gradual reevaluation)
# - but the last scheduled decision task should *not* timeout (workflow closed)
with freeze_time("2015-01-01 12:00:00"):
wfe = make_workflow_execution(
2016-02-02 19:02:37 +00:00
execution_start_to_close_timeout=8 * 60, task_start_to_close_timeout=5 * 60
)
# decision will automatically start
wfe = auto_start_decision_tasks(wfe)
wfe.start()
event_idx = len(wfe.events())
with freeze_time("2015-01-01 12:08:00"):
wfe._process_timeouts()
event_types = [e.event_type for e in wfe.events()[event_idx:]]
event_types.should.equal(
[
"DecisionTaskTimedOut",
"DecisionTaskScheduled",
"DecisionTaskStarted",
"WorkflowExecutionTimedOut",
]
)
def test_record_marker():
wfe = make_workflow_execution()
MARKER_EVENT_ATTRIBUTES = {"markerName": "example_marker"}
wfe.record_marker(123, MARKER_EVENT_ATTRIBUTES)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("MarkerRecorded")
last_event.event_attributes["markerName"].should.equal("example_marker")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
def test_start_timer():
wfe = make_workflow_execution()
START_TIMER_EVENT_ATTRIBUTES = {"startToFireTimeout": "10", "timerId": "abc123"}
with patch("moto.swf.models.workflow_execution.ThreadingTimer"):
wfe.start_timer(123, START_TIMER_EVENT_ATTRIBUTES)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("TimerStarted")
last_event.event_attributes["startToFireTimeout"].should.equal("10")
last_event.event_attributes["timerId"].should.equal("abc123")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
def test_start_timer_correctly_fires_timer_later():
wfe = make_workflow_execution()
START_TIMER_EVENT_ATTRIBUTES = {"startToFireTimeout": "60", "timerId": "abc123"}
# Patch thread's event with one that immediately resolves
with patch("threading.Event.wait"):
wfe.start_timer(123, START_TIMER_EVENT_ATTRIBUTES)
# Small wait to let both events populate
sleep(0.5)
second_to_last_event = wfe.events()[-2]
last_event = wfe.events()[-1]
second_to_last_event.event_type.should.equal("TimerFired")
second_to_last_event.event_attributes["timerId"].should.equal("abc123")
second_to_last_event.event_attributes["startedEventId"].should.equal(1)
last_event.event_type.should.equal("DecisionTaskScheduled")
def test_start_timer_fails_if_timer_already_started():
wfe = make_workflow_execution()
existing_timer = Mock(spec=ThreadingTimer)
existing_timer.is_alive.return_value = True
wfe._timers["abc123"] = Timer(existing_timer, 1)
START_TIMER_EVENT_ATTRIBUTES = {"startToFireTimeout": "10", "timerId": "abc123"}
wfe.start_timer(123, START_TIMER_EVENT_ATTRIBUTES)
last_event = wfe.events()[-1]
last_event.event_type.should.equal("StartTimerFailed")
last_event.event_attributes["cause"].should.equal("TIMER_ID_ALREADY_IN_USE")
last_event.event_attributes["timerId"].should.equal("abc123")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
def test_cancel_timer():
wfe = make_workflow_execution()
existing_timer = Mock(spec=ThreadingTimer)
existing_timer.is_alive.return_value = True
wfe._timers["abc123"] = Timer(existing_timer, 1)
wfe.cancel_timer(123, "abc123")
last_event = wfe.events()[-1]
last_event.event_type.should.equal("TimerCancelled")
last_event.event_attributes["startedEventId"].should.equal(1)
last_event.event_attributes["timerId"].should.equal("abc123")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
existing_timer.cancel.assert_called_once()
assert not wfe._timers.get("abc123")
def test_cancel_timer_fails_if_timer_not_found():
wfe = make_workflow_execution()
wfe.cancel_timer(123, "abc123")
last_event = wfe.events()[-1]
last_event.event_type.should.equal("CancelTimerFailed")
last_event.event_attributes["cause"].should.equal("TIMER_ID_UNKNOWN")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
def test_cancel_workflow():
wfe = make_workflow_execution()
wfe.open_counts["openDecisionTasks"] = 1
wfe.cancel(123, "I want to cancel")
last_event = wfe.events()[-1]
last_event.event_type.should.equal("WorkflowExecutionCanceled")
last_event.event_attributes["details"].should.equal("I want to cancel")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)
def test_cancel_workflow_fails_if_open_decision():
wfe = make_workflow_execution()
wfe.open_counts["openDecisionTasks"] = 2
wfe.cancel(123, "I want to cancel")
last_event = wfe.events()[-1]
last_event.event_type.should.equal("CancelWorkflowExecutionFailed")
last_event.event_attributes["cause"].should.equal("UNHANDLED_DECISION")
last_event.event_attributes["decisionTaskCompletedEventId"].should.equal(123)