Add basic get_execution_history implementation for Step Functions (#3507)
* Add format command to makefile * Refactor executions to be a attribute of StateMachine * Begin to add tests for execution history * Add tests for failed and successful event histories, with implementations * Add failure case to environment var check * Skip test if in server mode and update implementation coverage * Add conditional import for mock to cover python 2 * Refactor stop execution logic into StateMachine * Refactor event history environment variable into settings.py * Remove typing and os import
This commit is contained in:
parent
ffa7f2e41a
commit
48df5bd5af
@ -8115,7 +8115,7 @@
|
|||||||
- [X] describe_state_machine
|
- [X] describe_state_machine
|
||||||
- [ ] describe_state_machine_for_execution
|
- [ ] describe_state_machine_for_execution
|
||||||
- [ ] get_activity_task
|
- [ ] get_activity_task
|
||||||
- [ ] get_execution_history
|
- [X] get_execution_history
|
||||||
- [ ] list_activities
|
- [ ] list_activities
|
||||||
- [X] list_executions
|
- [X] list_executions
|
||||||
- [X] list_state_machines
|
- [X] list_state_machines
|
||||||
|
4
Makefile
4
Makefile
@ -20,12 +20,14 @@ lint:
|
|||||||
flake8 moto
|
flake8 moto
|
||||||
black --check moto/ tests/
|
black --check moto/ tests/
|
||||||
|
|
||||||
|
format:
|
||||||
|
black moto/ tests/
|
||||||
|
|
||||||
test-only:
|
test-only:
|
||||||
rm -f .coverage
|
rm -f .coverage
|
||||||
rm -rf cover
|
rm -rf cover
|
||||||
@pytest -sv --cov=moto --cov-report html ./tests/ $(TEST_EXCLUDE)
|
@pytest -sv --cov=moto --cov-report html ./tests/ $(TEST_EXCLUDE)
|
||||||
|
|
||||||
|
|
||||||
test: lint test-only
|
test: lint test-only
|
||||||
|
|
||||||
test_server:
|
test_server:
|
||||||
|
@ -4,3 +4,12 @@ TEST_SERVER_MODE = os.environ.get("TEST_SERVER_MODE", "0").lower() == "true"
|
|||||||
INITIAL_NO_AUTH_ACTION_COUNT = float(
|
INITIAL_NO_AUTH_ACTION_COUNT = float(
|
||||||
os.environ.get("INITIAL_NO_AUTH_ACTION_COUNT", float("inf"))
|
os.environ.get("INITIAL_NO_AUTH_ACTION_COUNT", float("inf"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_sf_execution_history_type():
|
||||||
|
"""
|
||||||
|
Determines which execution history events `get_execution_history` returns
|
||||||
|
:returns: str representing the type of Step Function Execution Type events should be
|
||||||
|
returned. Default value is SUCCESS, currently supports (SUCCESS || FAILURE)
|
||||||
|
"""
|
||||||
|
return os.environ.get("SF_EXECUTION_HISTORY_TYPE", "SUCCESS")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from dateutil.tz import tzlocal
|
||||||
|
|
||||||
from boto3 import Session
|
from boto3 import Session
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ from .exceptions import (
|
|||||||
StateMachineDoesNotExist,
|
StateMachineDoesNotExist,
|
||||||
)
|
)
|
||||||
from .utils import paginate, api_to_cfn_tags, cfn_to_api_tags
|
from .utils import paginate, api_to_cfn_tags, cfn_to_api_tags
|
||||||
|
from moto import settings
|
||||||
|
|
||||||
|
|
||||||
class StateMachine(CloudFormationModel):
|
class StateMachine(CloudFormationModel):
|
||||||
@ -27,10 +29,51 @@ class StateMachine(CloudFormationModel):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.definition = definition
|
self.definition = definition
|
||||||
self.roleArn = roleArn
|
self.roleArn = roleArn
|
||||||
|
self.executions = []
|
||||||
self.tags = []
|
self.tags = []
|
||||||
if tags:
|
if tags:
|
||||||
self.add_tags(tags)
|
self.add_tags(tags)
|
||||||
|
|
||||||
|
def start_execution(self, region_name, account_id, execution_name, execution_input):
|
||||||
|
self._ensure_execution_name_doesnt_exist(execution_name)
|
||||||
|
self._validate_execution_input(execution_input)
|
||||||
|
execution = Execution(
|
||||||
|
region_name=region_name,
|
||||||
|
account_id=account_id,
|
||||||
|
state_machine_name=self.name,
|
||||||
|
execution_name=execution_name,
|
||||||
|
state_machine_arn=self.arn,
|
||||||
|
execution_input=execution_input,
|
||||||
|
)
|
||||||
|
self.executions.append(execution)
|
||||||
|
return execution
|
||||||
|
|
||||||
|
def stop_execution(self, execution_arn):
|
||||||
|
execution = next(
|
||||||
|
(x for x in self.executions if x.execution_arn == execution_arn), None
|
||||||
|
)
|
||||||
|
if not execution:
|
||||||
|
raise ExecutionDoesNotExist(
|
||||||
|
"Execution Does Not Exist: '" + execution_arn + "'"
|
||||||
|
)
|
||||||
|
execution.stop()
|
||||||
|
return execution
|
||||||
|
|
||||||
|
def _ensure_execution_name_doesnt_exist(self, name):
|
||||||
|
for execution in self.executions:
|
||||||
|
if execution.name == name:
|
||||||
|
raise ExecutionAlreadyExists(
|
||||||
|
"Execution Already Exists: '" + execution.execution_arn + "'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _validate_execution_input(self, execution_input):
|
||||||
|
try:
|
||||||
|
json.loads(execution_input)
|
||||||
|
except Exception as ex:
|
||||||
|
raise InvalidExecutionInput(
|
||||||
|
"Invalid State Machine Execution Input: '" + str(ex) + "'"
|
||||||
|
)
|
||||||
|
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
if value is not None:
|
if value is not None:
|
||||||
@ -176,6 +219,104 @@ class Execution:
|
|||||||
self.status = "RUNNING"
|
self.status = "RUNNING"
|
||||||
self.stop_date = None
|
self.stop_date = None
|
||||||
|
|
||||||
|
def get_execution_history(self, roleArn):
|
||||||
|
sf_execution_history_type = settings.get_sf_execution_history_type()
|
||||||
|
if sf_execution_history_type == "SUCCESS":
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 0, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "ExecutionStarted",
|
||||||
|
"id": 1,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"executionStartedEventDetails": {
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
"roleArn": roleArn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "PassStateEntered",
|
||||||
|
"id": 2,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"stateEnteredEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "PassStateExited",
|
||||||
|
"id": 3,
|
||||||
|
"previousEventId": 2,
|
||||||
|
"stateExitedEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"output": "An output",
|
||||||
|
"outputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 20, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "ExecutionSucceeded",
|
||||||
|
"id": 4,
|
||||||
|
"previousEventId": 3,
|
||||||
|
"executionSucceededEventDetails": {
|
||||||
|
"output": "An output",
|
||||||
|
"outputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
elif sf_execution_history_type == "FAILURE":
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 0, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "ExecutionStarted",
|
||||||
|
"id": 1,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"executionStartedEventDetails": {
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
"roleArn": roleArn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "FailStateEntered",
|
||||||
|
"id": 2,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"stateEnteredEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": iso_8601_datetime_with_milliseconds(
|
||||||
|
datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal())
|
||||||
|
),
|
||||||
|
"type": "ExecutionFailed",
|
||||||
|
"id": 3,
|
||||||
|
"previousEventId": 2,
|
||||||
|
"executionFailedEventDetails": {
|
||||||
|
"error": "AnError",
|
||||||
|
"cause": "An error occurred!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.status = "ABORTED"
|
self.status = "ABORTED"
|
||||||
self.stop_date = iso_8601_datetime_with_milliseconds(datetime.now())
|
self.stop_date = iso_8601_datetime_with_milliseconds(datetime.now())
|
||||||
@ -346,38 +487,23 @@ class StepFunctionBackend(BaseBackend):
|
|||||||
return sm
|
return sm
|
||||||
|
|
||||||
def start_execution(self, state_machine_arn, name=None, execution_input=None):
|
def start_execution(self, state_machine_arn, name=None, execution_input=None):
|
||||||
state_machine_name = self.describe_state_machine(state_machine_arn).name
|
state_machine = self.describe_state_machine(state_machine_arn)
|
||||||
self._ensure_execution_name_doesnt_exist(name)
|
execution = state_machine.start_execution(
|
||||||
self._validate_execution_input(execution_input)
|
|
||||||
execution = Execution(
|
|
||||||
region_name=self.region_name,
|
region_name=self.region_name,
|
||||||
account_id=self._get_account_id(),
|
account_id=self._get_account_id(),
|
||||||
state_machine_name=state_machine_name,
|
|
||||||
execution_name=name or str(uuid4()),
|
execution_name=name or str(uuid4()),
|
||||||
state_machine_arn=state_machine_arn,
|
|
||||||
execution_input=execution_input,
|
execution_input=execution_input,
|
||||||
)
|
)
|
||||||
self.executions.append(execution)
|
|
||||||
return execution
|
return execution
|
||||||
|
|
||||||
def stop_execution(self, execution_arn):
|
def stop_execution(self, execution_arn):
|
||||||
execution = next(
|
self._validate_execution_arn(execution_arn)
|
||||||
(x for x in self.executions if x.execution_arn == execution_arn), None
|
state_machine = self._get_state_machine_for_execution(execution_arn)
|
||||||
)
|
return state_machine.stop_execution(execution_arn)
|
||||||
if not execution:
|
|
||||||
raise ExecutionDoesNotExist(
|
|
||||||
"Execution Does Not Exist: '" + execution_arn + "'"
|
|
||||||
)
|
|
||||||
execution.stop()
|
|
||||||
return execution
|
|
||||||
|
|
||||||
@paginate
|
@paginate
|
||||||
def list_executions(self, state_machine_arn, status_filter=None):
|
def list_executions(self, state_machine_arn, status_filter=None):
|
||||||
executions = [
|
executions = self.describe_state_machine(state_machine_arn).executions
|
||||||
execution
|
|
||||||
for execution in self.executions
|
|
||||||
if execution.state_machine_arn == state_machine_arn
|
|
||||||
]
|
|
||||||
|
|
||||||
if status_filter:
|
if status_filter:
|
||||||
executions = list(filter(lambda e: e.status == status_filter, executions))
|
executions = list(filter(lambda e: e.status == status_filter, executions))
|
||||||
@ -385,13 +511,32 @@ class StepFunctionBackend(BaseBackend):
|
|||||||
executions = sorted(executions, key=lambda x: x.start_date, reverse=True)
|
executions = sorted(executions, key=lambda x: x.start_date, reverse=True)
|
||||||
return executions
|
return executions
|
||||||
|
|
||||||
def describe_execution(self, arn):
|
def describe_execution(self, execution_arn):
|
||||||
self._validate_execution_arn(arn)
|
self._validate_execution_arn(execution_arn)
|
||||||
exctn = next((x for x in self.executions if x.execution_arn == arn), None)
|
state_machine = self._get_state_machine_for_execution(execution_arn)
|
||||||
|
exctn = next(
|
||||||
|
(x for x in state_machine.executions if x.execution_arn == execution_arn),
|
||||||
|
None,
|
||||||
|
)
|
||||||
if not exctn:
|
if not exctn:
|
||||||
raise ExecutionDoesNotExist("Execution Does Not Exist: '" + arn + "'")
|
raise ExecutionDoesNotExist(
|
||||||
|
"Execution Does Not Exist: '" + execution_arn + "'"
|
||||||
|
)
|
||||||
return exctn
|
return exctn
|
||||||
|
|
||||||
|
def get_execution_history(self, execution_arn):
|
||||||
|
self._validate_execution_arn(execution_arn)
|
||||||
|
state_machine = self._get_state_machine_for_execution(execution_arn)
|
||||||
|
execution = next(
|
||||||
|
(x for x in state_machine.executions if x.execution_arn == execution_arn),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if not execution:
|
||||||
|
raise ExecutionDoesNotExist(
|
||||||
|
"Execution Does Not Exist: '" + execution_arn + "'"
|
||||||
|
)
|
||||||
|
return execution.get_execution_history(state_machine.roleArn)
|
||||||
|
|
||||||
def tag_resource(self, resource_arn, tags):
|
def tag_resource(self, resource_arn, tags):
|
||||||
try:
|
try:
|
||||||
state_machine = self.describe_state_machine(resource_arn)
|
state_machine = self.describe_state_machine(resource_arn)
|
||||||
@ -444,20 +589,18 @@ class StepFunctionBackend(BaseBackend):
|
|||||||
if not arn or not match:
|
if not arn or not match:
|
||||||
raise InvalidArn(invalid_msg)
|
raise InvalidArn(invalid_msg)
|
||||||
|
|
||||||
def _ensure_execution_name_doesnt_exist(self, name):
|
def _get_state_machine_for_execution(self, execution_arn):
|
||||||
for execution in self.executions:
|
state_machine_name = execution_arn.split(":")[6]
|
||||||
if execution.name == name:
|
state_machine_arn = next(
|
||||||
raise ExecutionAlreadyExists(
|
(x.arn for x in self.state_machines if x.name == state_machine_name), None
|
||||||
"Execution Already Exists: '" + execution.execution_arn + "'"
|
|
||||||
)
|
)
|
||||||
|
if not state_machine_arn:
|
||||||
def _validate_execution_input(self, execution_input):
|
# Assume that if the state machine arn is not present, then neither will the
|
||||||
try:
|
# execution
|
||||||
json.loads(execution_input)
|
raise ExecutionDoesNotExist(
|
||||||
except Exception as ex:
|
"Execution Does Not Exist: '" + execution_arn + "'"
|
||||||
raise InvalidExecutionInput(
|
|
||||||
"Invalid State Machine Execution Input: '" + str(ex) + "'"
|
|
||||||
)
|
)
|
||||||
|
return self.describe_state_machine(state_machine_arn)
|
||||||
|
|
||||||
def _get_account_id(self):
|
def _get_account_id(self):
|
||||||
return ACCOUNT_ID
|
return ACCOUNT_ID
|
||||||
|
@ -208,6 +208,21 @@ class StepFunctionResponse(BaseResponse):
|
|||||||
@amzn_request_id
|
@amzn_request_id
|
||||||
def stop_execution(self):
|
def stop_execution(self):
|
||||||
arn = self._get_param("executionArn")
|
arn = self._get_param("executionArn")
|
||||||
|
try:
|
||||||
execution = self.stepfunction_backend.stop_execution(arn)
|
execution = self.stepfunction_backend.stop_execution(arn)
|
||||||
response = {"stopDate": execution.stop_date}
|
response = {"stopDate": execution.stop_date}
|
||||||
return 200, {}, json.dumps(response)
|
return 200, {}, json.dumps(response)
|
||||||
|
except AWSError as err:
|
||||||
|
return err.response()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
|
def get_execution_history(self):
|
||||||
|
execution_arn = self._get_param("executionArn")
|
||||||
|
try:
|
||||||
|
execution_history = self.stepfunction_backend.get_execution_history(
|
||||||
|
execution_arn
|
||||||
|
)
|
||||||
|
response = {"events": execution_history}
|
||||||
|
return 200, {}, json.dumps(response)
|
||||||
|
except AWSError as err:
|
||||||
|
return err.response()
|
||||||
|
@ -2,15 +2,23 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from dateutil.tz import tzlocal
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from moto import mock_cloudformation, mock_sts, mock_stepfunctions
|
from moto import mock_cloudformation, mock_sts, mock_stepfunctions
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
|
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
import mock
|
||||||
|
from unittest import SkipTest
|
||||||
|
else:
|
||||||
|
from unittest import SkipTest, mock
|
||||||
|
|
||||||
region = "us-east-1"
|
region = "us-east-1"
|
||||||
simple_definition = (
|
simple_definition = (
|
||||||
'{"Comment": "An example of the Amazon States Language using a choice state.",'
|
'{"Comment": "An example of the Amazon States Language using a choice state.",'
|
||||||
@ -799,10 +807,30 @@ def test_state_machine_stop_execution():
|
|||||||
|
|
||||||
@mock_stepfunctions
|
@mock_stepfunctions
|
||||||
@mock_sts
|
@mock_sts
|
||||||
def test_state_machine_describe_execution_after_stoppage():
|
def test_state_machine_stop_raises_error_when_unknown_execution():
|
||||||
account_id
|
client = boto3.client("stepfunctions", region_name=region)
|
||||||
|
client.create_state_machine(
|
||||||
|
name="test-state-machine",
|
||||||
|
definition=str(simple_definition),
|
||||||
|
roleArn=_get_default_role(),
|
||||||
|
)
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
unknown_execution = (
|
||||||
|
"arn:aws:states:"
|
||||||
|
+ region
|
||||||
|
+ ":"
|
||||||
|
+ _get_account_id()
|
||||||
|
+ ":execution:test-state-machine:unknown"
|
||||||
|
)
|
||||||
|
client.stop_execution(executionArn=unknown_execution)
|
||||||
|
ex.value.response["Error"]["Code"].should.equal("ExecutionDoesNotExist")
|
||||||
|
ex.value.response["Error"]["Message"].should.contain("Execution Does Not Exist:")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_stepfunctions
|
||||||
|
@mock_sts
|
||||||
|
def test_state_machine_describe_execution_after_stoppage():
|
||||||
client = boto3.client("stepfunctions", region_name=region)
|
client = boto3.client("stepfunctions", region_name=region)
|
||||||
#
|
|
||||||
sm = client.create_state_machine(
|
sm = client.create_state_machine(
|
||||||
name="name", definition=str(simple_definition), roleArn=_get_default_role()
|
name="name", definition=str(simple_definition), roleArn=_get_default_role()
|
||||||
)
|
)
|
||||||
@ -815,6 +843,146 @@ def test_state_machine_describe_execution_after_stoppage():
|
|||||||
description["stopDate"].should.be.a(datetime)
|
description["stopDate"].should.be.a(datetime)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_stepfunctions
|
||||||
|
@mock_sts
|
||||||
|
def test_state_machine_get_execution_history_throws_error_with_unknown_execution():
|
||||||
|
client = boto3.client("stepfunctions", region_name=region)
|
||||||
|
client.create_state_machine(
|
||||||
|
name="test-state-machine",
|
||||||
|
definition=str(simple_definition),
|
||||||
|
roleArn=_get_default_role(),
|
||||||
|
)
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
unknown_execution = (
|
||||||
|
"arn:aws:states:"
|
||||||
|
+ region
|
||||||
|
+ ":"
|
||||||
|
+ _get_account_id()
|
||||||
|
+ ":execution:test-state-machine:unknown"
|
||||||
|
)
|
||||||
|
client.get_execution_history(executionArn=unknown_execution)
|
||||||
|
ex.value.response["Error"]["Code"].should.equal("ExecutionDoesNotExist")
|
||||||
|
ex.value.response["Error"]["Message"].should.contain("Execution Does Not Exist:")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_stepfunctions
|
||||||
|
@mock_sts
|
||||||
|
def test_state_machine_get_execution_history_contains_expected_success_events_when_started():
|
||||||
|
expected_events = [
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 0, tzinfo=tzlocal()),
|
||||||
|
"type": "ExecutionStarted",
|
||||||
|
"id": 1,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"executionStartedEventDetails": {
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
"roleArn": _get_default_role(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal()),
|
||||||
|
"type": "PassStateEntered",
|
||||||
|
"id": 2,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"stateEnteredEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal()),
|
||||||
|
"type": "PassStateExited",
|
||||||
|
"id": 3,
|
||||||
|
"previousEventId": 2,
|
||||||
|
"stateExitedEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"output": "An output",
|
||||||
|
"outputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 20, tzinfo=tzlocal()),
|
||||||
|
"type": "ExecutionSucceeded",
|
||||||
|
"id": 4,
|
||||||
|
"previousEventId": 3,
|
||||||
|
"executionSucceededEventDetails": {
|
||||||
|
"output": "An output",
|
||||||
|
"outputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
client = boto3.client("stepfunctions", region_name=region)
|
||||||
|
sm = client.create_state_machine(
|
||||||
|
name="test-state-machine",
|
||||||
|
definition=simple_definition,
|
||||||
|
roleArn=_get_default_role(),
|
||||||
|
)
|
||||||
|
execution = client.start_execution(stateMachineArn=sm["stateMachineArn"])
|
||||||
|
execution_history = client.get_execution_history(
|
||||||
|
executionArn=execution["executionArn"]
|
||||||
|
)
|
||||||
|
execution_history["events"].should.have.length_of(4)
|
||||||
|
execution_history["events"].should.equal(expected_events)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_stepfunctions
|
||||||
|
@mock_sts
|
||||||
|
@mock.patch.dict(os.environ, {"SF_EXECUTION_HISTORY_TYPE": "FAILURE"})
|
||||||
|
def test_state_machine_get_execution_history_contains_expected_failure_events_when_started():
|
||||||
|
if os.environ.get("TEST_SERVER_MODE", "false").lower() == "true":
|
||||||
|
raise SkipTest("Cant pass environment variable in server mode")
|
||||||
|
expected_events = [
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 0, tzinfo=tzlocal()),
|
||||||
|
"type": "ExecutionStarted",
|
||||||
|
"id": 1,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"executionStartedEventDetails": {
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
"roleArn": _get_default_role(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal()),
|
||||||
|
"type": "FailStateEntered",
|
||||||
|
"id": 2,
|
||||||
|
"previousEventId": 0,
|
||||||
|
"stateEnteredEventDetails": {
|
||||||
|
"name": "A State",
|
||||||
|
"input": "{}",
|
||||||
|
"inputDetails": {"truncated": False},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": datetime(2020, 1, 1, 0, 0, 10, tzinfo=tzlocal()),
|
||||||
|
"type": "ExecutionFailed",
|
||||||
|
"id": 3,
|
||||||
|
"previousEventId": 2,
|
||||||
|
"executionFailedEventDetails": {
|
||||||
|
"error": "AnError",
|
||||||
|
"cause": "An error occurred!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
client = boto3.client("stepfunctions", region_name=region)
|
||||||
|
sm = client.create_state_machine(
|
||||||
|
name="test-state-machine",
|
||||||
|
definition=simple_definition,
|
||||||
|
roleArn=_get_default_role(),
|
||||||
|
)
|
||||||
|
execution = client.start_execution(stateMachineArn=sm["stateMachineArn"])
|
||||||
|
execution_history = client.get_execution_history(
|
||||||
|
executionArn=execution["executionArn"]
|
||||||
|
)
|
||||||
|
execution_history["events"].should.have.length_of(3)
|
||||||
|
execution_history["events"].should.equal(expected_events)
|
||||||
|
|
||||||
|
|
||||||
@mock_stepfunctions
|
@mock_stepfunctions
|
||||||
@mock_cloudformation
|
@mock_cloudformation
|
||||||
def test_state_machine_cloudformation():
|
def test_state_machine_cloudformation():
|
||||||
|
Loading…
Reference in New Issue
Block a user