693 lines
25 KiB
Python
693 lines
25 KiB
Python
from threading import Timer as ThreadingTimer
|
|
from time import sleep
|
|
|
|
from freezegun import freeze_time
|
|
from unittest.mock import Mock, patch
|
|
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,
|
|
)
|
|
|
|
|
|
VALID_ACTIVITY_TASK_ATTRIBUTES = {
|
|
"activityId": "my-activity-001",
|
|
"activityType": {"name": "test-activity", "version": "v1.1"},
|
|
"taskList": {"name": "task-list-name"},
|
|
"scheduleToStartTimeout": "600",
|
|
"scheduleToCloseTimeout": "600",
|
|
"startToCloseTimeout": "600",
|
|
"heartbeatTimeout": "300",
|
|
}
|
|
|
|
|
|
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")
|
|
|
|
|
|
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)
|
|
|
|
|
|
def test_workflow_execution_string_representation():
|
|
wfe = make_workflow_execution(child_policy="TERMINATE")
|
|
str(wfe).should.match(r"^WorkflowExecution\(run_id: .*\)")
|
|
|
|
|
|
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)
|
|
|
|
|
|
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")
|
|
|
|
|
|
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"])
|
|
|
|
|
|
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",
|
|
}
|
|
)
|
|
|
|
|
|
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)
|
|
|
|
|
|
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")
|
|
|
|
|
|
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])
|
|
|
|
|
|
@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")
|
|
|
|
|
|
@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")
|
|
|
|
|
|
@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")
|
|
|
|
|
|
@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)
|
|
|
|
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")
|
|
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)
|
|
|
|
|
|
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(
|
|
123,
|
|
{
|
|
"activityId": "my-activity-001",
|
|
"activityType": {"name": "test-activity", "version": "v1.2"},
|
|
"scheduleToStartTimeout": "600",
|
|
"scheduleToCloseTimeout": "600",
|
|
"startToCloseTimeout": "600",
|
|
"heartbeatTimeout": "300",
|
|
},
|
|
)
|
|
|
|
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)
|
|
|
|
|
|
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",
|
|
"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")
|
|
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")
|
|
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")
|
|
last_event.event_attributes["cause"].should.equal("DEFAULT_TASK_LIST_UNDEFINED")
|
|
|
|
hsh["taskList"] = {"name": "foobar"}
|
|
wfe.schedule_activity_task(123, hsh)
|
|
last_event = wfe.events()[-1]
|
|
last_event.event_type.should.equal("ScheduleActivityTaskFailed")
|
|
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")
|
|
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")
|
|
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")
|
|
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)
|
|
|
|
|
|
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)
|
|
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")
|
|
|
|
|
|
def test_workflow_execution_schedule_activity_task_with_same_activity_id():
|
|
wfe = make_workflow_execution()
|
|
|
|
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")
|
|
|
|
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")
|
|
last_event.event_attributes["cause"].should.equal("ACTIVITY_ID_ALREADY_IN_USE")
|
|
|
|
|
|
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")
|
|
|
|
|
|
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)
|
|
|
|
|
|
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")
|
|
|
|
|
|
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)
|
|
|
|
|
|
# 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(
|
|
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)
|