diff --git a/moto/stepfunctions/exceptions.py b/moto/stepfunctions/exceptions.py index 704e4ea83..6000bab4e 100644 --- a/moto/stepfunctions/exceptions.py +++ b/moto/stepfunctions/exceptions.py @@ -18,6 +18,11 @@ class AWSError(Exception): ) +class ExecutionAlreadyExists(AWSError): + TYPE = "ExecutionAlreadyExists" + STATUS = 400 + + class ExecutionDoesNotExist(AWSError): TYPE = "ExecutionDoesNotExist" STATUS = 400 diff --git a/moto/stepfunctions/models.py b/moto/stepfunctions/models.py index e36598f23..58b6bb434 100644 --- a/moto/stepfunctions/models.py +++ b/moto/stepfunctions/models.py @@ -8,6 +8,7 @@ from moto.core.utils import iso_8601_datetime_without_milliseconds from moto.sts.models import ACCOUNT_ID from uuid import uuid4 from .exceptions import ( + ExecutionAlreadyExists, ExecutionDoesNotExist, InvalidArn, InvalidName, @@ -205,6 +206,7 @@ class StepFunctionBackend(BaseBackend): def start_execution(self, state_machine_arn, name=None): state_machine_name = self.describe_state_machine(state_machine_arn).name + self._ensure_execution_name_doesnt_exist(name) execution = Execution( region_name=self.region_name, account_id=self._get_account_id(), @@ -278,6 +280,13 @@ class StepFunctionBackend(BaseBackend): if not arn or not match: raise InvalidArn(invalid_msg) + 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 _get_account_id(self): return ACCOUNT_ID diff --git a/requirements-dev.txt b/requirements-dev.txt index 313f2dfb6..e40a568a5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ -r requirements.txt nose -black; python_version >= '3.6' +black==19.10b0; python_version >= '3.6' regex==2019.11.1; python_version >= '3.6' # Needed for black sure==1.4.11 coverage==4.5.4 diff --git a/setup.py b/setup.py index 707a56212..ffaa8b273 100755 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ install_requires = [ "werkzeug", "PyYAML>=5.1", "pytz", + "ecdsa<0.15", "python-dateutil<3.0.0,>=2.1", "python-jose[cryptography]>=3.1.0,<4.0.0", "docker>=2.5.1", diff --git a/tests/test_stepfunctions/test_stepfunctions.py b/tests/test_stepfunctions/test_stepfunctions.py index 4324964d8..043fd9bfb 100644 --- a/tests/test_stepfunctions/test_stepfunctions.py +++ b/tests/test_stepfunctions/test_stepfunctions.py @@ -404,6 +404,27 @@ def test_state_machine_start_execution_with_custom_name(): execution["startDate"].should.be.a(datetime) +@mock_stepfunctions +@mock_sts +def test_state_machine_start_execution_fails_on_duplicate_execution_name(): + client = boto3.client("stepfunctions", region_name=region) + # + sm = client.create_state_machine( + name="name", definition=str(simple_definition), roleArn=_get_default_role() + ) + execution_one = client.start_execution( + stateMachineArn=sm["stateMachineArn"], name="execution_name" + ) + # + with assert_raises(ClientError) as exc: + _ = client.start_execution( + stateMachineArn=sm["stateMachineArn"], name="execution_name" + ) + exc.exception.response["Error"]["Message"].should.equal( + "Execution Already Exists: '" + execution_one["executionArn"] + "'" + ) + + @mock_stepfunctions @mock_sts def test_state_machine_list_executions():